Part #2 of task cancellation hooks.

* Expose new EvaluateTaskConfig throughout all the APIs
* Create a key for cancellation configuration
* Add default values for cancellation in GlobalPlugin
* Create a test to ensure that cancellation can cancel tasks.
* Deprecate all the existing mechanisms of evaluating tasks which
  use the EvaluateConfig API.
This commit is contained in:
Josh Suereth 2014-04-08 09:23:49 -04:00
parent 6e480fc2b6
commit 171eb19b96
9 changed files with 73 additions and 8 deletions

View File

@ -59,7 +59,7 @@ final object Aggregation
import extracted.structure
val toRun = ts map { case KeyValue(k,t) => t.map(v => KeyValue(k,v)) } join;
val roots = ts map { case KeyValue(k,_) => k }
val config = extractedConfig(extracted, structure, s)
val config = extractedTaskConfig(extracted, structure, s)
val start = System.currentTimeMillis
val (newS, result) = withStreams(structure, s){ str =>

View File

@ -120,6 +120,10 @@ object Defaults extends BuildCommon
trapExit :== true,
connectInput :== false,
cancelable :== false,
taskCancelHandler := { state: State =>
if(cancelable.value) TaskEvaluationCancelHandler.Signal
else TaskEvaluationCancelHandler.Null
},
envVars :== Map.empty,
sbtVersion := appConfiguration.value.provider.id.version,
sbtBinaryVersion := binarySbtVersion(sbtVersion.value),

View File

@ -79,6 +79,19 @@ final object EvaluateTaskConfig {
}
AdaptedTaskConfig
}
/** Raw constructor for EvaluateTaskConfig. */
def apply(restrictions: Seq[Tags.Rule],
checkCycles: Boolean,
progressReporter: ExecuteProgress[Task],
cancelHandler: TaskEvaluationCancelHandler): EvaluateTaskConfig = {
object SimpleEvaluateTaskConfig extends EvaluateTaskConfig {
def restrictions = restrictions
def checkCycles = checkCycles
def progressReporter = progressReporter
def cancelHandler = cancelHandler
}
SimpleEvaluateTaskConfig
}
}
final case class PluginData(dependencyClasspath: Seq[Attributed[File]], definitionClasspath: Seq[Attributed[File]], resolvers: Option[Seq[Resolver]], report: Option[UpdateReport], scalacOptions: Seq[String])
@ -106,24 +119,25 @@ object EvaluateTask
val SystemProcessors = Runtime.getRuntime.availableProcessors
@deprecated("Use extractedConfig.", "0.13.0")
@deprecated("Use extractedTaskConfig.", "0.13.0")
def defaultConfig(state: State): EvaluateConfig =
{
val extracted = Project.extract(state)
extractedConfig(extracted, extracted.structure, state)
}
@deprecated("Use extractedConfig.", "0.13.0")
@deprecated("Use extractedTaskConfig.", "0.13.0")
def defaultConfig(extracted: Extracted, structure: BuildStructure) =
EvaluateConfig(false, restrictions(extracted, structure), progress = defaultProgress)
@deprecated("Use other extractedConfig", "0.13.2")
@deprecated("Use other extractedTaskConfig", "0.13.2")
def extractedConfig(extracted: Extracted, structure: BuildStructure): EvaluateConfig =
{
val workers = restrictions(extracted, structure)
val canCancel = cancelable(extracted, structure)
EvaluateConfig(cancelable = canCancel, restrictions = workers, progress = defaultProgress)
}
@deprecated("Use other extractedTaskConfig", "0.13.5")
def extractedConfig(extracted: Extracted, structure: BuildStructure, state: State): EvaluateConfig =
{
val workers = restrictions(extracted, structure)
@ -131,6 +145,13 @@ object EvaluateTask
val progress = executeProgress(extracted, structure, state)
EvaluateConfig(cancelable = canCancel, restrictions = workers, progress = progress)
}
def extractedTaskConfig(extracted: Extracted, structure: BuildStructure, state: State): EvaluateTaskConfig =
{
val rs = restrictions(extracted, structure)
val canceller = cancelHandler(extracted, structure, state)
val progress = executeProgress(extracted, structure, state)
EvaluateTaskConfig(rs, false, progress, canceller)
}
def defaultRestrictions(maxWorkers: Int) = Tags.limitAll(maxWorkers) :: Nil
def defaultRestrictions(extracted: Extracted, structure: BuildStructure): Seq[Tags.Rule] =
@ -150,11 +171,13 @@ object EvaluateTask
1
def cancelable(extracted: Extracted, structure: BuildStructure): Boolean =
getSetting(Keys.cancelable, false, extracted, structure)
def cancelHandler(extracted: Extracted, structure: BuildStructure, state: State): TaskEvaluationCancelHandler =
getSetting(Keys.taskCancelHandler, {(_: State) => TaskEvaluationCancelHandler.Null}, extracted, structure)(state)
private[sbt] def executeProgress(extracted: Extracted, structure: BuildStructure, state: State): ExecuteProgress[Task] = {
import Types.const
val maker: State => Keys.TaskProgress = getSetting(Keys.executeProgress, const(new Keys.TaskProgress(defaultProgress)), extracted, structure)
maker(state).progress
val maker: State => Keys.TaskProgress = getSetting(Keys.executeProgress, const(new Keys.TaskProgress(defaultProgress)), extracted, structure)
maker(state).progress
}
def getSetting[T](key: SettingKey[T], default: T, extracted: Extracted, structure: BuildStructure): T =
@ -185,16 +208,20 @@ object EvaluateTask
* If the task is not defined, None is returned. The provided task key is resolved against the current project `ref`.
* Task execution is configured according to settings defined in the loaded project.*/
def apply[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef): Option[(State, Result[T])] =
apply[T](structure, taskKey, state, ref, extractedConfig(Project.extract(state), structure))
apply[T](structure, taskKey, state, ref, extractedTaskConfig(Project.extract(state), structure, state))
/** Evaluates `taskKey` and returns the new State and the result of the task wrapped in Some.
* If the task is not defined, None is returned. The provided task key is resolved against the current project `ref`.
* `config` configures concurrency and canceling of task execution. */
@deprecated("Use EvalauteTaskConfig option instead.", "0.13.5")
def apply[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, config: EvaluateConfig): Option[(State, Result[T])] =
apply(structure, taskKey, state, ref, EvaluateTaskConfig(config))
def apply[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, config: EvaluateTaskConfig): Option[(State, Result[T])] = {
withStreams(structure, state) { str =>
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
runTask(task, state, str, structure.index.triggers, config)(toNode)
}
}
def logIncResult(result: Result[_], state: State, streams: Streams) = result match { case Inc(i) => logIncomplete(i, state, streams); case _ => () }
def logIncomplete(result: Incomplete, state: State, streams: Streams)
{

View File

@ -346,6 +346,7 @@ object Keys
// wrapper to work around SI-2915
private[sbt] final class TaskProgress(val progress: ExecuteProgress[Task])
private[sbt] val executeProgress = SettingKey[State => TaskProgress]("executeProgress", "Experimental task execution listener.", DTask)
private[sbt] val taskCancelHandler = SettingKey[State => TaskEvaluationCancelHandler]("taskCancelHandler", "Experimental task cancellation handler.", DTask)
// Experimental in sbt 0.13.2 to enable grabing semantic compile failures.
private[sbt] val compilerReporter = TaskKey[Option[xsbti.Reporter]]("compilerReporter", "Experimental hook to listen (or send) compilation failure messages.", DTask)

View File

@ -498,13 +498,25 @@ object Project extends ProjectExtra
@deprecated("This method does not apply state changes requested during task execution. Use 'runTask' instead, which does.", "0.11.1")
def evaluateTask[T](taskKey: ScopedKey[Task[T]], state: State, checkCycles: Boolean = false, maxWorkers: Int = EvaluateTask.SystemProcessors): Option[Result[T]] =
runTask(taskKey, state, EvaluateConfig(true, EvaluateTask.defaultRestrictions(maxWorkers), checkCycles)).map(_._2)
def runTask[T](taskKey: ScopedKey[Task[T]], state: State, checkCycles: Boolean = false): Option[(State, Result[T])] =
runTask(taskKey, state, EvaluateConfig(true, EvaluateTask.restrictions(state), checkCycles))
{
val extracted = Project.extract(state)
val ch = EvaluateTask.cancelHandler(extracted, extracted.structure, state)
val p = EvaluateTask.executeProgress(extracted, extracted.structure, state)
val r = EvaluateTask.restrictions(state)
runTask(taskKey, state, EvaluateTaskConfig(r, checkCycles, p, ch))
}
@deprecated("Use EvalauteTaskConfig option instead.", "0.13.5")
def runTask[T](taskKey: ScopedKey[Task[T]], state: State, config: EvaluateConfig): Option[(State, Result[T])] =
{
val extracted = Project.extract(state)
EvaluateTask(extracted.structure, taskKey, state, extracted.currentRef, config)
}
def runTask[T](taskKey: ScopedKey[Task[T]], state: State, config: EvaluateTaskConfig): Option[(State, Result[T])] = {
val extracted = Project.extract(state)
EvaluateTask(extracted.structure, taskKey, state, extracted.currentRef, config)
}
implicit def projectToRef(p: Project): ProjectReference = LocalProject(p.id)

View File

@ -0,0 +1,8 @@
import sbt.ExposeYourself._
taskCancelHandler := { (state: State) =>
new TaskEvaluationCancelHandler {
override def start(canceller: TaskCancel): Unit = canceller.cancel()
override def finish(): Unit = ()
}
}

View File

@ -0,0 +1,5 @@
package sbt // this API is private[sbt], so only exposed for trusted clients and folks who like breaking.
object ExposeYourself {
val taskCancelHandler = sbt.Keys.taskCancelHandler
}

View File

@ -0,0 +1,5 @@
import scala
object Foo {
val x = "this should be long to compile or the test may fail."
}

View File

@ -0,0 +1,3 @@
# All tasks should fail.
-> compile
-> test