This patch adds support for conditional execution of steps.  The decision
of whether to execute a particular step is based on the step's "doStepIf"
parameter.  That parameter defaults to True, which means the step should
run.  It can be set to boolean value, or to a function that returns a 
boolean value.  If doStepIf is False, or if it is a function that returns
False, then the step will immediately set its result to SKIPPED, and it
will not execute.

If doStepIf is set to a function or lambda, that function or lambda should
accept a single parameter, which will be the step object itself.

diff -ur ./buildbot/process/buildstep.py ../buildbot-0.7.10p1.new/buildbot/process/buildstep.py
--- ./buildbot/process/buildstep.py	2009-02-20 15:14:20.000000000 -0600
+++ ../buildbot-0.7.10p1.new/buildbot/process/buildstep.py	2009-02-20 14:41:45.000000000 -0600
@@ -564,6 +564,7 @@
              'warnOnWarnings',
              'warnOnFailure',
              'progressMetrics',
+             'doStepIf',
              ]
 
     name = "generic"
@@ -573,6 +574,8 @@
     build = None
     step_status = None
     progress = None
+    # doStepIf can be False, True, or a function that returns False or True
+    doStepIf = True
 
     def __init__(self, **kwargs):
         self.factory = (self.__class__, dict(kwargs))
@@ -702,7 +705,16 @@
             self.progress.start()
         self.step_status.stepStarted()
         try:
-            skip = self.start()
+            skip = None
+            if isinstance(self.doStepIf, bool):
+                if self.doStepIf == False:
+                    skip = SKIPPED
+            elif self.doStepIf(self) == False:
+                skip = SKIPPED
+
+            if skip is None:
+                skip = self.start()
+
             if skip == SKIPPED:
                 reactor.callLater(0, self.releaseLocks)
                 reactor.callLater(0, self.deferred.callback, SKIPPED)
@@ -760,7 +772,15 @@
         If the step decides it does not need to be run, start() can return
         the constant SKIPPED. This fires the callback immediately: it is not
         necessary to call .finished yourself. This can also indicate to the
-        status-reporting mechanism that this step should not be displayed."""
+        status-reporting mechanism that this step should not be displayed.
+
+        A step can be configured to only run under certain conditions.  To
+        do this, set the step's doStepIf to a boolean value, or to a function
+        that returns a boolean value.  If the value or function result is
+        False, then the step will return SKIPPED without doing anything,
+        otherwise the step will be executed normally.  If you set doStepIf
+        to a function, that function should accept one parameter, which will
+        be the Step object itself."""
         
         raise NotImplementedError("your subclass must implement this method")
 
diff -ur ./buildbot/test/test_steps.py ../buildbot-0.7.10p1.new/buildbot/test/test_steps.py
--- ./buildbot/test/test_steps.py	2008-09-16 11:02:34.000000000 -0500
+++ ../buildbot-0.7.10p1.new/buildbot/test/test_steps.py	2009-02-20 15:12:53.000000000 -0600
@@ -24,7 +24,7 @@
 from buildbot.buildslave import BuildSlave
 from buildbot.steps import shell, source, python
 from buildbot.status import builder
-from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE
+from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, SKIPPED
 from buildbot.test.runutils import RunMixin, rmtree
 from buildbot.test.runutils import makeBuildStep, StepTester
 from buildbot.slave import commands, registry
@@ -786,3 +786,53 @@
         d.addCallback(_check)
         reactor.callLater(0, d.callback, None)
         return d
+
+class SuccessStep(buildstep.BuildStep):
+    def start(self):
+        self.finished(buildstep.SUCCESS)
+
+class ConditionalStepTest(StepTester, unittest.TestCase):
+    def testNotSkipped(self):
+        self.slavebase = "testNotSkipped.slave"
+        self.masterbase = "testNotSkipped.master"
+        sb = self.makeSlaveBuilder()
+        step = self.makeStep(SuccessStep)
+        d = self.runStep(step)
+        def _checkResults(results):
+            self.failUnlessEqual(SUCCESS, results)
+        d.addCallback(_checkResults)
+        return d
+
+    def testSkipped(self):
+        self.slavebase = "testSkipped.slave"
+        self.masterbase = "testSkipped.master"
+        sb = self.makeSlaveBuilder()
+        step = self.makeStep(SuccessStep, doStepIf=False)
+        d = self.runStep(step)
+        def _checkResults(results):
+            self.failUnlessEqual(SKIPPED, results)
+        d.addCallback(_checkResults)
+        return d
+
+    def testNotSkippedFunc(self):
+        self.slavebase = "testNotSkippedFunc.slave"
+        self.masterbase = "testNotSkippedFunc.master"
+        sb = self.makeSlaveBuilder()
+        step = self.makeStep(SuccessStep, doStepIf=lambda s: True)
+        d = self.runStep(step)
+        def _checkResults(results):
+            self.failUnlessEqual(SUCCESS, results)
+        d.addCallback(_checkResults)
+        return d
+
+    def testSkippedFunc(self):
+        self.slavebase = "testSkippedFunc.slave"
+        self.masterbase = "testSkippedFunc.master"
+        sb = self.makeSlaveBuilder()
+        step = self.makeStep(SuccessStep, doStepIf=lambda s: False)
+        d = self.runStep(step)
+        def _checkResults(results):
+            self.failUnlessEqual(SKIPPED, results)
+        d.addCallback(_checkResults)
+        return d
+
