mirror of https://github.com/sbt/sbt.git
support for task hooks: triggeredBy and runBefore
This commit is contained in:
parent
324c832dee
commit
f24af2a05b
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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[_]]
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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[_] )
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue