1
2
3 from twisted.internet import reactor
4 from twisted.spread import pb
5 from twisted.python import log
6 from buildbot import util
7
9 """I keep track of how much progress a single BuildStep has made.
10
11 Progress is measured along various axes. Time consumed is one that is
12 available for all steps. Amount of command output is another, and may be
13 better quantified by scanning the output for markers to derive number of
14 files compiled, directories walked, tests run, etc.
15
16 I am created when the build begins, and given to a BuildProgress object
17 so it can track the overall progress of the whole build.
18
19 """
20
21 startTime = None
22 stopTime = None
23 expectedTime = None
24 buildProgress = None
25 debug = False
26
34
37
39 """The step can call this to explicitly set a target value for one
40 of its metrics. E.g., ShellCommands knows how many commands it will
41 execute, so it could set the 'commands' expectation."""
42 for metric, value in metrics.items():
43 self.expectations[metric] = value
44 self.buildProgress.newExpectations()
45
49
53
55 """The step calls this as progress is made along various axes."""
56 if self.debug:
57 print "setProgress[%s][%s] = %s" % (self.name, metric, value)
58 self.progress[metric] = value
59 if self.debug:
60 r = self.remaining()
61 print " step remaining:", r
62 self.buildProgress.newProgress()
63
65 """This stops the 'time' metric and marks the step as finished
66 overall. It should be called after the last .setProgress has been
67 done for each axis."""
68 if self.debug: print "StepProgress.finish[%s]" % self.name
69 self.stopTime = util.now()
70 self.buildProgress.stepFinished(self.name)
71
75
76 - def remaining(self):
77 if self.startTime == None:
78 return self.expectedTime
79 if self.stopTime != None:
80 return 0
81
82
83
84
85
86
87
88
89
90
91
92
93
94 percentages = []
95 for metric, value in self.progress.items():
96 expectation = self.expectations[metric]
97 if value != None and expectation != None:
98 p = 1.0 * value / expectation
99 percentages.append(p)
100 if percentages:
101 avg = reduce(lambda x,y: x+y, percentages) / len(percentages)
102 if avg > 1.0:
103
104 avg = 1.0
105 if avg < 0.0:
106 avg = 0.0
107 if percentages and self.expectedTime != None:
108 return self.expectedTime - (avg * self.expectedTime)
109 if self.expectedTime is not None:
110
111 return self.expectedTime - (util.now() - self.startTime)
112 return None
113
114
117 self.interval = interval
118 self.timer = None
119 self.needUpdate = 0
120
122 """I keep track of overall build progress. I hold a list of StepProgress
123 objects.
124 """
125
134
141
143 """Call this when one of the steps has changed its expectations.
144 This should trigger us to update our ETA value and notify any
145 subscribers."""
146 pass
147
149 assert(stepname not in self.finishedSteps)
150 self.finishedSteps.append(stepname)
151 if len(self.finishedSteps) == len(self.steps.keys()):
152 self.sendLastUpdates()
153
160
161 - def remaining(self):
162
163 sum = 0
164 for name, step in self.steps.items():
165 rem = step.remaining()
166 if rem == None:
167 return None
168 sum += rem
169 return sum
176
177
191
230
243
244
246 debug = False
247
248
249
250 decay = 0.5
251
270
271 - def wavg(self, old, current):
272 if old is None:
273 return current
274 if current is None:
275 return old
276 else:
277 return (current * self.decay) + (old * (1 - self.decay))
278
279 - def update(self, buildprogress):
280 for name, stepprogress in buildprogress.steps.items():
281 old = self.times[name]
282 current = stepprogress.totalTime()
283 if current == None:
284 log.msg("Expectations.update: current[%s] was None!" % name)
285 continue
286 new = self.wavg(old, current)
287 self.times[name] = new
288 if self.debug:
289 print "new expected time[%s] = %s, old %s, cur %s" % \
290 (name, new, old, current)
291
292 for metric, current in stepprogress.progress.items():
293 old = self.steps[name][metric]
294 new = self.wavg(old, current)
295 if self.debug:
296 print "new expectation[%s][%s] = %s, old %s, cur %s" % \
297 (name, metric, new, old, current)
298 self.steps[name][metric] = new
299
301 if None in self.times.values():
302 return None
303
304
305 s = 0
306 for v in self.times.values():
307 s += v
308 return s
309