From 4ae0ba6b57eb30a62639843cdce9f67df3bf1372 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Thu, 21 Jul 2011 22:03:56 -0400 Subject: [PATCH] more refactoring of special settings: input tasks. fixes #114 --- main/Aggregation.scala | 17 +++-------------- main/EvaluateTask.scala | 4 +--- main/Keys.scala | 2 +- main/Load.scala | 28 ++++++++++++++++++---------- main/Structure.scala | 13 +++++++++---- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/main/Aggregation.scala b/main/Aggregation.scala index ccba7ad3d..2c8aa6fbd 100644 --- a/main/Aggregation.scala +++ b/main/Aggregation.scala @@ -6,7 +6,7 @@ package sbt import CommandSupport.logger import Project.ScopedKey import Load.BuildStructure - import Keys.{aggregate, parseResult, showSuccess, showTiming, timingFormat} + import Keys.{aggregate, showSuccess, showTiming, timingFormat} import sbt.complete.Parser import java.net.URI import Parser._ @@ -129,29 +129,18 @@ final object Aggregation } 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 inputMap = (Map.empty[AnyRef,Any] /: (inputs zip parseds)) { case (im, (id, v)) => im + ((id.value.defined, v.value)) } + val dummies = Dummies( InputTask.inputMap :^: KNil, inputMap :+: 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 { diff --git a/main/EvaluateTask.scala b/main/EvaluateTask.scala index e87994f40..a6e183874 100644 --- a/main/EvaluateTask.scala +++ b/main/EvaluateTask.scala @@ -6,7 +6,7 @@ package sbt import java.io.File import Project.{ScopedKey, Setting} import Keys.{streams, Streams, TaskStreams} - import Keys.{dummyState, dummyStreamsManager, parseResult, streamsManager, taskDefinitionKey} + import Keys.{dummyState, dummyStreamsManager, streamsManager, taskDefinitionKey} import Scope.{GlobalScope, ThisScope} import scala.Console.{RED, RESET} @@ -131,8 +131,6 @@ object EvaluateTask stream.open() stream }) - else if(scoped.key == parseResult.key) - Seq(parseResult in scoped.scope := error("Unsubstituted parse result for " + Project.display(scoped)) ) else Nil } diff --git a/main/Keys.scala b/main/Keys.scala index 0071ee8e3..464b3c5fe 100644 --- a/main/Keys.scala +++ b/main/Keys.scala @@ -271,7 +271,7 @@ object Keys val (state, dummyState) = dummy[State]("state", "Current build state.") val (streamsManager, dummyStreamsManager) = dummy[Streams]("streams-manager", "Streams manager, which provides streams for different contexts.") 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.") + private[sbt] val parseResult: TaskKey[Any] = TaskKey("$parse-result", "Internal: used to implement input tasks.") val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by") val runBefore = AttributeKey[Seq[Task[_]]]("run-before") diff --git a/main/Load.scala b/main/Load.scala index 2fda54464..d32beb3c1 100644 --- a/main/Load.scala +++ b/main/Load.scala @@ -123,17 +123,17 @@ object Load (rootEval, new BuildStructure(projects, loaded.root, settings, data, index, streams, delegates, config.scopeLocal)) } - // map dependencies on the special tasks so that the scope is the same as the defining key - // additionally, set the task axis to the defining key if it is not set + // map dependencies on the special tasks: + // 1. the scope of 'streams' is the same as the defining key and has the task axis set to the defining key + // 2. the defining key is stored on constructed tasks + // 3. resolvedScoped is replaced with the defining key as a value + // 4. parseResult is replaced with a task that provides the result of parsing for the defined InputTask + // Note: this must be idempotent. def finalTransforms(ss: Seq[Setting[_]]): Seq[Setting[_]] = { - def isSpecial(key: AttributeKey[_]) = key == streams.key || key == parseResult.key def mapSpecial(to: ScopedKey[_]) = new (ScopedKey ~> ScopedKey){ def apply[T](key: ScopedKey[T]) = - if(isSpecial(key.key)) - { - val replaced = Scope.replaceThis(to.scope)(key.scope) - ScopedKey(Scope.fillTaskAxis(replaced, to.key), key.key) - } + if(key.key == streams.key) + ScopedKey(Scope.fillTaskAxis(Scope.replaceThis(to.scope)(key.scope), to.key), key.key) else key } def setDefining[T] = (key: ScopedKey[T], value: T) => value match { @@ -142,9 +142,17 @@ object Load case _ => value } def setResolved(defining: ScopedKey[_]) = new (ScopedKey ~> Option) { def apply[T](key: ScopedKey[T]): Option[T] = - if(key.key == resolvedScoped.key) Some(defining.asInstanceOf[T]) else None + key.key match + { + case resolvedScoped.key => Some(defining.asInstanceOf[T]) + case parseResult.key => + import std.TaskExtra._ + val getResult = InputTask.inputMap map { m => m get defining getOrElse error("No parsed value for " + Project.display(defining) + "\n" + m) } + Some(getResult.asInstanceOf[T]) + case _ => None + } } - ss.map(s => s mapReferenced mapSpecial(s.key) mapInit setDefining mapConstant setResolved(s.key)) + ss.map(s => s mapConstant setResolved(s.key) mapReferenced mapSpecial(s.key) mapInit setDefining ) } def setDefinitionKey[T](tk: Task[T], key: ScopedKey[_]): Task[T] = if(isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work) diff --git a/main/Structure.scala b/main/Structure.scala index bfe93d58c..26c48886d 100644 --- a/main/Structure.scala +++ b/main/Structure.scala @@ -24,11 +24,13 @@ private sealed trait InputDynamic[T] extends InputTask[T] { outer => type Result def parser: State => Parser[Result] + def defined: ScopedKey[_] def task: Task[T] def mapTask[S](f: Task[T] => Task[S]) = new InputDynamic[S] { type Result = outer.Result def parser = outer.parser def task = f(outer.task) + def defined = outer.defined } } object InputTask @@ -44,20 +46,23 @@ object InputTask def separate[I,T](p: Initialize[State => Parser[I]])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = p.zipWith(action)((parser, act) => free(parser)(act)) + private[sbt] lazy val inputMap: Task[Map[AnyRef,Any]] = mktask { error("Internal sbt error: input map not substituted.") } // This interface allows the Parser to be constructed using other Settings, but not Tasks (which is desired). // The action can be constructed using Settings and Tasks and with the parse result injected into a Task. - // This is the ugly part, requiring hooks in injectStreams and Act to handle the dummy task for the parse result. - // However, this is results in a minimal interface to the full capabilities of an InputTask for users + // This is the ugly part, requiring hooks in Load.finalTransforms and Aggregation.applyDynamicTasks + // to handle the dummy task for the parse result. + // However, this results in a minimal interface to the full capabilities of an InputTask for users def apply[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] = { - val key = Keys.parseResult.asInstanceOf[TaskKey[I]] - p.zipWith(action(key)) { (parserF, act) => + val key: TaskKey[I] = Keys.parseResult.asInstanceOf[TaskKey[I]] + (p zip Keys.resolvedScoped.identity zipWith action(key)) { case ((parserF, scoped), act) => new InputDynamic[T] { type Result = I def parser = parserF def task = act + def defined = scoped } } }