aggregation

This commit is contained in:
Mark Harrah 2011-02-21 10:07:39 -05:00
parent 02d3c6bcad
commit 218ccc2c9f
4 changed files with 118 additions and 34 deletions

View File

@ -63,36 +63,13 @@ object Act
def optProjectRef(index: KeyIndex, currentBuild: URI, currentProject: String) =
projectRef(index, currentBuild) ?? ProjectRef(Some(currentBuild), Some(currentProject))
def valueParser(s: State, structure: BuildStructure, show: Boolean)(key: ScopedKey[_]): Parser[() => State] =
structure.data.get(key.scope, key.key) match
{
case None => failure("Invalid setting or task")
case Some(input: InputStatic[_]) => applyTask(s, structure, input.parser(s), show)
case Some(input: InputDynamic[_]) => applyDynamicTask(s, structure, input, show)
case Some(task: Task[_]) => applyTask(s, structure, success(task), show)
case Some(v) => success(() => { logger(s).info(v.toString); s})
}
def applyDynamicTask[I](s: State, structure: Load.BuildStructure, input: InputDynamic[I], show: Boolean): Parser[() => State] =
Command.applyEffect(input parser s) { parsed =>
import EvaluateTask._
val result = withStreams(structure){ str => runTask(input.task)(nodeView(s, str, parsed)) }
processResult(result, logger(s), show)
s
}
def applyTask(s: State, structure: Load.BuildStructure, p: Parser[Task[_]], show: Boolean): Parser[() => State] =
Command.applyEffect(p) { t =>
import EvaluateTask._
val result = withStreams(structure){ str => runTask(t)(nodeView(s, str)) }
processResult(result, logger(s), show)
s
}
def actParser(s: State): Parser[() => State] = requireSession(s, actParser0(s))
private[this] def actParser0(state: State) =
{
val extracted = Project extract state
showParser.flatMap { show =>
scopedKeyParser(extracted) flatMap valueParser(state, extracted.structure, show)
scopedKeyParser(extracted) flatMap Aggregation.valueParser(state, extracted.structure, show)
}
}
def showParser = token( ("show" ~ Space) ^^^ true) ?? false

109
main/Aggregation.scala Normal file
View File

@ -0,0 +1,109 @@
/* sbt -- Simple Build Tool
* Copyright 2011 Mark Harrah
*/
package sbt
import CommandSupport.logger
import Project.ScopedKey
import Load.BuildStructure
import EvaluateTask.parseResult
import Keys.Aggregate
import sbt.complete.Parser
import Parser._
sealed trait Aggregation
final object Aggregation
{
def apply(dependencies: Seq[ProjectRef], transitive: Boolean = true): Aggregation = new Explicit(dependencies, transitive)
implicit def fromBoolean(b: Boolean): Aggregation = if(b) Enabled else Disabled
val Enabled = new Implicit(true)
val Disabled = new Implicit(false)
final case class Implicit(enabled: Boolean) extends Aggregation
final class Explicit(val dependencies: Seq[ProjectRef], val transitive: Boolean) extends Aggregation
final case class KeyValue[+T](key: ScopedKey[_], value: T)
def getTasks[T](key: ScopedKey[T], structure: BuildStructure, transitive: Boolean): Seq[KeyValue[T]] =
{
val task = structure.data.get(key.scope, key.key).toList.map(t => KeyValue(key,t))
if(transitive) aggregate(key, structure) ++ task else task
}
def projectAggregate(key: ScopedKey[_], structure: BuildStructure): Seq[ProjectRef] =
{
val project = key.scope.project.toOption.flatMap { p => Project.getProject(p, structure) }
project match { case Some(p) => p.aggregate; case None => Nil }
}
def aggregate[T](key: ScopedKey[T], structure: BuildStructure): Seq[KeyValue[T]] =
{
val aggregated = Aggregate in Scope.fillTaskAxis(key.scope, key.key) get structure.data getOrElse Enabled
val (agg, transitive) =
aggregated match
{
case Implicit(false) => (Nil, false)
case Implicit(true) => (projectAggregate(key, structure), true)
case e: Explicit => (e.dependencies, e.transitive)
}
agg flatMap { a =>
val newKey = ScopedKey(key.scope.copy(project = Select(a)), key.key)
getTasks(newKey, structure, transitive)
}
}
def printSettings[T](xs: Seq[KeyValue[T]], log: Logger) =
xs match
{
case KeyValue(_,x) :: Nil => log.info(x.toString)
case _ => xs foreach { case KeyValue(key, value) => log.info(Project.display(key) + "\n\t" + value.toString) }
}
type Values[T] = Seq[KeyValue[T]]
def seqParser[T](ps: Values[Parser[T]]): Parser[Seq[KeyValue[T]]] = seq(ps.map { case KeyValue(k,p) => p.map(v => KeyValue(k,v) ) })
def applyTasks[T](s: State, structure: BuildStructure, ps: Values[Parser[Task[T]]], show: Boolean): Parser[() => State] =
Command.applyEffect(seqParser(ps)) { ts =>
runTasks(s, structure, ts, Dummies(KNil, HNil), show)
s
}
def runTasks[HL <: HList, T](s: State, structure: Load.BuildStructure, ts: Values[Task[T]], extra: Dummies[HL], show: Boolean)
{
import EvaluateTask._
import std.TaskExtra._
val toRun = ts map { case KeyValue(k,t) => t.map(v => KeyValue(k,v)) } join;
val result = withStreams(structure){ str => runTask(toRun)(nodeView(s, str, extra.tasks, extra.values)) }
val log = logger(s)
onResult(result, log)( results => if(show) printSettings(results, log))
}
final case class Dummies[HL <: HList](tasks: KList[Task,HL], values: HL)
private[this] def dummyMap[HL <: HList, I](vs: Values[I], data: Settings[Scope], dummies: Dummies[HL]): Dummies[HL2] forSome { type HL2 <: HList } =
vs match
{
case Seq() => dummies
case Seq(kv: KeyValue[t], xs @ _*) =>
val dummyParsed = dummyParsedTask(kv.key, data).asInstanceOf[Task[t]]
dummyMap(xs, data, Dummies(KCons(dummyParsed, dummies.tasks), HCons(kv.value, dummies.values)))
}
def applyDynamicTasks[I](s: State, structure: BuildStructure, inputs: Values[InputDynamic[I]], show: Boolean): Parser[() => State] =
{
val parsers = inputs.map { case KeyValue(k,t) => KeyValue(k, t parser s) }
Command.applyEffect(seqParser(parsers)) { parseds =>
import EvaluateTask._
val dummies = dummyMap(parseds, structure.data, Dummies(KNil, HNil))
val roots = inputs.map { case KeyValue(k,t) => KeyValue(k,t.task) }
runTasks(s, structure, roots, dummies, show)
s
}
}
private[this] def dummyParsedTask(key: ScopedKey[_], data: Settings[Scope]): Task[_] =
data.get(Scope.fillTaskAxis(key.scope, key.key), parseResult.key) getOrElse error("Parsed result dummy task not found in " + Project.display(key))
def valueParser(s: State, structure: BuildStructure, show: Boolean)(key: ScopedKey[_]): Parser[() => State] =
getTasks(key, structure, true).toList match
{
case Nil => failure("Invalid setting or task")
case xs @ KeyValue(_, _: InputStatic[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[InputStatic[t]]])(_.parser(s)), show)
case xs @ KeyValue(_, _: InputDynamic[t]) :: _ => applyDynamicTasks(s, structure, xs.asInstanceOf[Values[InputDynamic[t]]], show)
case xs @ KeyValue(_, _: Task[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[Task[t]]])(x => success(x)), show)
case xs => success(() => { printSettings(xs, logger(s)); s} )
}
private[this] def maps[T, S](vs: Values[T])(f: T => S): Values[S] =
vs map { case KeyValue(k,v) => KeyValue(k, f(v)) }
}

View File

@ -109,10 +109,7 @@ object EvaluateTask
val (streamsManager, dummyStreamsManager) = dummy[Streams]("streams-manager")
val streams = TaskKey[TaskStreams]("streams")
val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped")
// will be cast to the appropriate type when passed to an InputTask implementation
// this assumes there is only one InputTask finally selected and will need to be
// revisited for aggregating InputTasks
private[sbt] val (parseResult, dummyParseResult) = dummy[Any]("$parse-result")
private[sbt] val parseResult: TaskKey[_] = TaskKey("$parse-result")
def injectSettings: Seq[Project.Setting[_]] = Seq(
(state in Scope.GlobalScope) ::= dummyState,
@ -148,8 +145,8 @@ object EvaluateTask
for( t <- structure.data.get(resolvedScope, taskKey.key)) yield
(t, nodeView(state, streams))
}
def nodeView(state: State, streams: Streams, parsed: Any = ()): Execute.NodeView[Task] =
Transform(dummyParseResult :^: dummyStreamsManager :^: dummyState :^: KNil, parsed :+: streams :+: state :+: HNil)
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], checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
{
@ -160,11 +157,11 @@ object EvaluateTask
}
def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T =
onResult(result, log) { v => if(show) println("Result: " + v); v }
def onResult[T, S](result: Result[T], log: Logger)(f: T => S): S =
result match
{
case Value(v) =>
if(show) println("Result: " + v)
v
case Value(v) => f(v)
case Inc(inc) =>
log.error(Incomplete.show(inc, true))
error("Task did not complete successfully")
@ -181,7 +178,7 @@ object EvaluateTask
else if(scoped.key == resolvedScoped.key)
Seq(resolvedScoped in scoped.scope :== scoped)
else if(scoped.key == parseResult.key)
Seq(parseResult in scoped.scope ::= dummyParseResult)
Seq(parseResult in scoped.scope := error("Unsubstituted parse result for " + Project.display(scoped)) )
else
Nil
}

View File

@ -118,6 +118,7 @@ sealed trait ScopeAxis[+S] {
case Global => ifGlobal
case Select(s) => f(s)
}
def toOption: Option[S] = foldStrict(Some.apply, None, None)
def map[T](f: S => T): ScopeAxis[T] = foldStrict(s => Select(f(s)), Global, This)
}
case object This extends ScopeAxis[Nothing]