more refactoring of special settings: input tasks. fixes #114

This commit is contained in:
Mark Harrah 2011-07-21 22:03:56 -04:00
parent fb9e3bd516
commit 4ae0ba6b57
5 changed files with 32 additions and 32 deletions

View File

@ -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
{

View File

@ -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
}

View File

@ -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")

View File

@ -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)

View File

@ -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
}
}
}