1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  from twisted.python import log, failure 
 17  from twisted.internet import reactor 
 18  from buildbot.process.buildstep import BuildStep 
 19  from buildbot.status import builder, buildstep 
 20  from buildbot import config 
 21   
 23      """Raised by Blocker when it is passed an upstream step that cannot 
 24      be found or is in a bad state.""" 
 25      pass 
  26   
 28      """ 
 29      Build step that blocks until at least one other step finishes. 
 30   
 31      @ivar upstreamSteps: a non-empty list of (builderName, stepName) tuples 
 32                           identifying the other build steps that must 
 33                           complete in order to unblock this Blocker. 
 34      @ivar idlePolicy: string: what to do if one of the upstream builders is 
 35                        idle when this Blocker starts; one of: 
 36                          \"error\": just blow up (the Blocker will fail with 
 37                            status EXCEPTION) 
 38                          \"ignore\": carry on as if the referenced build step 
 39                            was not mentioned (or is already complete) 
 40                          \"block\": block until the referenced builder starts 
 41                            a build, and then block until the referenced build 
 42                            step in that build finishes 
 43      @ivar timeout: int: how long to block, in seconds, before giving up and 
 44                     failing (default: None, meaning block forever) 
 45      """ 
 46      parms = (BuildStep.parms + 
 47               ['upstreamSteps', 
 48                'idlePolicy', 
 49                'timeout', 
 50               ]) 
 51   
 52      flunkOnFailure = True                
 53      upstreamSteps = None 
 54      idlePolicy = "block" 
 55      timeout = None 
 56   
 57      VALID_IDLE_POLICIES = ("error", "ignore", "block") 
 58   
 86   
 89   
 91          return "<%s %x: %s>" % (self.__class__.__name__, id(self), self.name) 
  92   
 93 -    def _log(self, message, *args): 
  94          log.msg(repr(self) + ": " + (message % args)) 
  95   
 97          """ 
 98          Return true if buildStatus1 and buildStatus2 are from related 
 99          builds, i.e. a Blocker step running in buildStatus2 should be 
100          blocked by an upstream step in buildStatus1.  Return false if 
101          they are unrelated. 
102   
103          Default implementation simply raises NotImplementedError: you 
104          *must* subclass Blocker and implement this method, because 
105          BuildBot currently provides no way to relate different builders. 
106          This might change if ticket #875 (\"build flocks\") is 
107          implemented. 
108          """ 
109          raise NotImplementedError( 
110              "abstract method: you must subclass Blocker " 
111              "and implement buildsMatch()") 
 112   
114          try: 
115               
116               
117               
118               
119              builder = botmaster.builders[builderName] 
120          except KeyError: 
121              raise BadStepError( 
122                  "no builder named %r" % builderName) 
123   
124           
125           
126           
127          myBuildStatus = self.build.getStatus() 
128          builderStatus = builder.builder_status 
129          matchingBuild = None 
130   
131           
132           
133           
134           
135           
136           
137           
138           
139           
140           
141          all_builds = (builderStatus.buildCache.values() + 
142                        builderStatus.getCurrentBuilds()) 
143   
144          for buildStatus in all_builds: 
145              if self.buildsMatch(myBuildStatus, buildStatus): 
146                  matchingBuild = buildStatus 
147                  break 
148   
149          if matchingBuild is None: 
150              msg = "no matching builds found in builder %r" % builderName 
151              if self.idlePolicy == "error": 
152                  raise BadStepError(msg + " (is it idle?)") 
153              elif self.idlePolicy == "ignore": 
154                   
155                  self._log(msg + ": skipping it") 
156                  return None 
157              elif self.idlePolicy == "block": 
158                  self._log(msg + ": will block until it starts a build") 
159                  self._blocking_builders.add(builderStatus) 
160                  return None 
161   
162          self._log("found builder %r: %r", builderName, builder) 
163          return matchingBuild 
 164   
174   
184   
186          return [self.name+":", "blocking on"] + self._getFullnames() 
 187   
188 -    def _getFinishStatusText(self, code, elapsed): 
 189          meaning = builder.Results[code] 
190          text = [self.name+":", 
191                  "upstream %s" % meaning, 
192                  "after %.1f sec" % elapsed] 
193          if code != builder.SUCCESS: 
194              text += self._getFullnames() 
195          return text 
 196   
198          return [self.name+":", "timed out", "(%.1f sec)" % self.timeout] 
 199   
201          self.step_status.setText(self._getBlockingStatusText()) 
202   
203          if self.timeout is not None: 
204              self._timer = reactor.callLater(self.timeout, self._timeoutExpired) 
205   
206          self._log("searching for upstream build steps") 
207          botmaster = self.build.slavebuilder.slave.parent 
208          errors = []                      
209          for (builderName, stepName) in self.upstreamSteps: 
210              buildStatus = stepStatus = None 
211              try: 
212                  buildStatus = self._getBuildStatus(botmaster, builderName) 
213                  if buildStatus is not None: 
214                      stepStatus = self._getStepStatus(buildStatus, stepName) 
215              except BadStepError, err: 
216                  errors.append(err.message) 
217              if stepStatus is not None: 
218                   
219                   
220                   
221                  self._blocking_steps.append(stepStatus) 
222   
223          if len(errors) == 1: 
224              raise BadStepError(errors[0]) 
225          elif len(errors) > 1: 
226              raise BadStepError("multiple errors:\n" + "\n".join(errors)) 
227   
228          self._log("will block on %d upstream build steps: %r", 
229                    len(self._blocking_steps), self._blocking_steps) 
230          if self._blocking_builders: 
231              self._log("will also block on %d builders starting a build: %r", 
232                        len(self._blocking_builders), self._blocking_builders) 
233   
234           
235           
236           
237           
238           
239           
240          for stepStatus in self._blocking_steps[:]: 
241              self._awaitStepFinished(stepStatus) 
242          self._log("after registering for each upstream build step, " 
243                    "_blocking_steps = %r", 
244                    self._blocking_steps) 
245   
246           
247          for bs in self._blocking_builders: 
248              bs.subscribe(BuilderStatusReceiver(self, bs)) 
 249   
251           
252           
253           
254          d = stepStatus.waitUntilFinished() 
255          d.addCallback(self._upstreamStepFinished) 
 256   
270   
272          assert isinstance(stepStatus, buildstep.BuildStepStatus) 
273          self._log("upstream build step %s:%s finished; results=%r", 
274                    stepStatus.getBuild().builder.getName(), 
275                    stepStatus.getName(), 
276                    stepStatus.getResults()) 
277   
278          if self._timed_out: 
279               
280              self._blocking_steps.remove(stepStatus) 
281              return 
282   
283          (code, text) = stepStatus.getResults() 
284          if code != builder.SUCCESS and self._overall_code == builder.SUCCESS: 
285               
286              self._overall_code = code 
287          self._overall_text.extend(text) 
288          self._log("now _overall_code=%r, _overall_text=%r", 
289                    self._overall_code, self._overall_text) 
290   
291          self._blocking_steps.remove(stepStatus) 
292          self._checkFinished() 
 293   
295          assert isinstance(builderStatus, builder.BuilderStatus) 
296          self._log("builder %r (%r) started a build; buildStatus=%r", 
297                    builderStatus, builderStatus.getName(), buildStatus) 
298   
299          myBuildStatus = self.build.getStatus() 
300          if not self.buildsMatch(myBuildStatus, buildStatus): 
301              self._log("but the just-started build does not match: " 
302                        "ignoring it") 
303              return 
304   
305          builderStatus.unsubscribe(receiver) 
306   
307           
308           
309           
310          new_blocking_steps = [] 
311          for (builderName, stepName) in self.upstreamSteps: 
312              if builderName == builderStatus.getName(): 
313                  try: 
314                      stepStatus = self._getStepStatus(buildStatus, stepName) 
315                  except BadStepError: 
316                      self.failed(failure.Failure()) 
317                       
318                       
319                       
320                  else: 
321                      new_blocking_steps.append(stepStatus) 
322   
323          self._blocking_steps.extend(new_blocking_steps) 
324          for stepStatus in new_blocking_steps: 
325              self._awaitStepFinished(stepStatus) 
326   
327          self._blocking_builders.remove(builderStatus) 
328          self._checkFinished() 
 329   
331          if self.step_status.isFinished(): 
332               
333               
334              self._log("_checkFinished: already finished, so nothing to do here") 
335              return 
336   
337          self._log("_checkFinished: _blocking_steps=%r, _blocking_builders=%r", 
338                    self._blocking_steps, self._blocking_builders) 
339   
340          if not self._blocking_steps and not self._blocking_builders: 
341              if self.timeout: 
342                  self._timer.cancel() 
343   
344              self.finished(self._overall_code) 
345              self.step_status.setText2(self._overall_text) 
346              (start, finish) = self.step_status.getTimes() 
347              self.step_status.setText( 
348                  self._getFinishStatusText(self._overall_code, finish - start)) 
  349   
351 -    def __init__(self, blocker, builderStatus): 
 352           
353           
354          self.blocker = blocker 
355          self.builderStatus = builderStatus 
 356   
359   
361          log.msg("BuilderStatusReceiver: " 
362                  "apparently, builder %r has started build %r" 
363                  % (name, buildStatus)) 
364          self.blocker._upstreamBuildStarted(self.builderStatus, buildStatus, self) 
 365