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