From 218ccc2c9f093f4f1b574169df8afe04ed1da687 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 21 Feb 2011 10:07:39 -0500 Subject: [PATCH] aggregation --- main/Act.scala | 25 +--------- main/Aggregation.scala | 109 +++++++++++++++++++++++++++++++++++++++++ main/Build.scala | 17 +++---- main/Scope.scala | 1 + 4 files changed, 118 insertions(+), 34 deletions(-) create mode 100644 main/Aggregation.scala diff --git a/main/Act.scala b/main/Act.scala index f3d21e891..46323ac48 100644 --- a/main/Act.scala +++ b/main/Act.scala @@ -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 diff --git a/main/Aggregation.scala b/main/Aggregation.scala new file mode 100644 index 000000000..0cf09db13 --- /dev/null +++ b/main/Aggregation.scala @@ -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)) } +} \ No newline at end of file diff --git a/main/Build.scala b/main/Build.scala index 5cb2fcfe2..91a085cc9 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -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 } diff --git a/main/Scope.scala b/main/Scope.scala index ad8739131..5962f9dda 100644 --- a/main/Scope.scala +++ b/main/Scope.scala @@ -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]