support for task hooks: triggeredBy and runBefore

This commit is contained in:
Mark Harrah 2011-04-23 11:49:58 -04:00
parent 324c832dee
commit f24af2a05b
7 changed files with 51 additions and 10 deletions

View File

@ -80,7 +80,7 @@ final object Aggregation
import std.TaskExtra._
val toRun = ts map { case KeyValue(k,t) => t.map(v => KeyValue(k,v)) } join;
val start = System.currentTimeMillis
val result = withStreams(structure){ str => runTask(toRun, str)(nodeView(s, str, extra.tasks, extra.values)) }
val result = withStreams(structure){ str => runTask(toRun, str, structure.index.triggers)(nodeView(s, str, extra.tasks, extra.values)) }
val stop = System.currentTimeMillis
val log = logger(s)
lazy val extracted = Project.extract(s)

View File

@ -123,6 +123,22 @@ object Index
else
error(duplicates.mkString("AttributeKey ID collisions detected for '", "', '", "'"))
}
private[this] type TriggerMap = collection.mutable.HashMap[Task[_], Seq[Task[_]]]
def triggers(ss: Settings[Scope]): Triggers[Task] =
{
val runBefore = new TriggerMap
val triggeredBy = new TriggerMap
for( (_, amap) <- ss.data; AttributeEntry(_, value: Task[_]) <- amap.entries)
{
val as = value.info.attributes
update(runBefore, value, as get Keys.runBefore)
update(triggeredBy, value, as get Keys.triggeredBy)
}
new Triggers[Task](runBefore, triggeredBy)
}
private[this] def update(map: TriggerMap, base: Task[_], tasksOpt: Option[Seq[Task[_]]]): Unit =
for( tasks <- tasksOpt; task <- tasks )
map(task) = base +: map.getOrElse(task, Nil)
}
object BuildStreams
{

View File

@ -35,7 +35,7 @@ object EvaluateTask
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
withStreams(structure) { str =>
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
runTask(task, str, checkCycles, maxWorkers)(toNode)
runTask(task, str, structure.index.triggers, checkCycles, maxWorkers)(toNode)
}
def logIncResult(result: Result[_], streams: Streams) = result match { case Inc(i) => logIncomplete(i, streams); case _ => () }
def logIncomplete(result: Incomplete, streams: Streams)
@ -71,11 +71,11 @@ object EvaluateTask
def nodeView[HL <: HList](state: State, streams: Streams, extraDummies: KList[Task, HL] = KNil, extraValues: HL = HNil): Execute.NodeView[Task] =
Transform(dummyStreamsManager :^: KCons(dummyState, extraDummies), streams :+: HCons(state, extraValues))
def runTask[Task[_] <: AnyRef, T](root: Task[T], streams: Streams, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
def runTask[Task[_] <: AnyRef, T](root: Task[T], streams: Streams, triggers: Triggers[Task], checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
{
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
val x = new Execute[Task](checkCycles)(taskToNode)
val x = new Execute[Task](checkCycles, triggers)(taskToNode)
val result = try { x.run(root)(service) } finally { shutdown() }
val replaced = transformInc(result)
logIncResult(replaced, streams)

View File

@ -234,6 +234,9 @@ object Keys
val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped", "The ScopedKey for the referencing setting or task.")
private[sbt] val parseResult: TaskKey[_] = TaskKey("$parse-result", "Internal: used to implement input tasks.")
val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by")
val runBefore = AttributeKey[Seq[Task[_]]]("run-before")
type Streams = std.Streams[ScopedKey[_]]
type TaskStreams = std.TaskStreams[ScopedKey[_]]

View File

@ -120,7 +120,7 @@ object Load
if(isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)
def structureIndex(settings: Settings[Scope]): StructureIndex =
new StructureIndex(Index.stringToKeyMap(settings), Index.taskToKeyMap(settings), KeyIndex(settings.allKeys( (s,k) => ScopedKey(s,k))))
new StructureIndex(Index.stringToKeyMap(settings), Index.taskToKeyMap(settings), Index.triggers(settings), KeyIndex(settings.allKeys( (s,k) => ScopedKey(s,k))))
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
def reapply(newSettings: Seq[Setting[_]], structure: BuildStructure): BuildStructure =
@ -444,7 +444,7 @@ object Load
}
final case class LoadBuildConfiguration(stagingDirectory: File, commonPluginClasspath: Seq[Attributed[File]], classpath: Seq[File], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: Seq[Setting[_]], log: Logger)
// information that is not original, but can be reconstructed from the rest of BuildStructure
final class StructureIndex(val keyMap: Map[String, AttributeKey[_]], val taskToKey: Map[Task[_], ScopedKey[Task[_]]], val keyIndex: KeyIndex)
final class StructureIndex(val keyMap: Map[String, AttributeKey[_]], val taskToKey: Map[Task[_], ScopedKey[Task[_]]], val triggers: Triggers[Task], val keyIndex: KeyIndex)
private[this] def memo[A,B](f: A => B): A => B =
{

View File

@ -213,6 +213,22 @@ object Scoped
def dependOn: Initialize[Task[Unit]] = Apply.tasks(KList.fromList(keys)) { kl => nop.dependsOn(kl.toList :_*) }
}
implicit def richInitializeTask[T](init: Initialize[Task[T]]): RichInitializeTask[T] = new RichInitializeTask(init)
final class RichInitializeTask[T](init: Initialize[Task[T]])
{
def triggeredBy(tasks: ScopedTask[_]*): Initialize[Task[T]] = nonLocal(tasks, Keys.triggeredBy)
def runBefore(tasks: ScopedTask[_]*): Initialize[Task[T]] = nonLocal(tasks, Keys.runBefore)
private[this] def nonLocal(tasks: Seq[ScopedTask[_]], key: AttributeKey[Seq[Task[_]]]): Initialize[Task[T]] =
{
val getTasks = Apply.tasks(KList.fromList(tasks))(idFun)
(getTasks zipWith init) { (tasks, i) =>
i.copy(info = i.info.set(key, tasks.toList))
}
}
}
implicit def richFileSetting(s: ScopedSetting[File]): RichFileSetting = new RichFileSetting(s)
implicit def richFilesSetting(s: ScopedSetting[Seq[File]]): RichFilesSetting = new RichFilesSetting(s)

View File

@ -26,9 +26,9 @@ object Execute
sealed trait Completed {
def process(): Unit
}
final class Triggers[A[_]](val runBefore: collection.Map[A[_], Seq[A[_]]], val injectFor: collection.Map[A[_], Seq[A[_]]])
final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeView[A] )
final class Execute[A[_] <: AnyRef](checkCycles: Boolean, triggers: Triggers[A])(implicit view: NodeView[A])
{
type Strategy = CompletionService[A[_], Completed]
@ -119,6 +119,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
state(node) = Done
remove( reverse, node ) foreach { dep => notifyDone(node, dep) }
callers.remove( node ).flatten.foreach { c => retire(c, callerResult(c, result)) }
triggeredBy( node ) foreach { t => addChecked(t) }
post {
assert( done(node) )
@ -126,6 +127,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
readyInv( node )
assert( ! (reverse contains node) )
assert( ! (callers contains node) )
assert( triggeredBy(node) forall added )
}
}
def callerResult[T](node: A[T], result: Result[T]): Result[T] =
@ -161,7 +163,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
pre { newPre(node) }
val v = register( node )
val deps = dependencies(v)
val deps = dependencies(v) ++ runBefore(node)
val active = IDSet[A[_]](deps filter notDone )
if( active.isEmpty)
@ -241,6 +243,10 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
def dependencies(node: A[_]): Iterable[A[_]] = dependencies(viewCache(node))
def dependencies(v: Node[A, _]): Iterable[A[_]] = v.uniformIn ++ v.mixedIn.toList
def runBefore(node: A[_]): Seq[A[_]] = getSeq(triggers.runBefore, node)
def triggeredBy(node: A[_]): Seq[A[_]] = getSeq(triggers.injectFor, node)
def getSeq(map: collection.Map[A[_], Seq[A[_]]], node: A[_]): Seq[A[_]] = map.getOrElse(node, Nil)
// Contracts
def addedInv(node: A[_]): Unit = topologicalSort(node) foreach addedCheck
@ -262,7 +268,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
def pendingInv(node: A[_])
{
assert( atState(node, Pending) )
assert( dependencies( node ) exists notDone )
assert( (dependencies( node ) ++ runBefore(node) ) exists notDone )
}
def runningInv( node: A[_] )
{