Remove InputStatic and parsedResult.

This cleans up the InputTask implementation.  It no longer requires the hook
in setting loading (Load.finalTransforms) and has better types.
This commit is contained in:
Mark Harrah 2013-01-18 18:49:21 -05:00
parent f4e8ff870b
commit 9013e00fdd
6 changed files with 81 additions and 52 deletions

View File

@ -10,7 +10,6 @@ object Def extends Init[Scope] with TaskMacroExtra
val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by") val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by")
val runBefore = AttributeKey[Seq[Task[_]]]("run-before") val runBefore = AttributeKey[Seq[Task[_]]]("run-before")
private[sbt] val parseResult: TaskKey[Any] = TaskKey("$parse-result", "Internal: used to implement input tasks.", KeyRanks.Invisible)
val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped", "The ScopedKey for the referencing setting or task.", KeyRanks.DSetting) val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped", "The ScopedKey for the referencing setting or task.", KeyRanks.DSetting)
lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None) lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None)

View File

@ -7,25 +7,21 @@ package sbt
import Types._ import Types._
/** Parses input and produces a task to run. Constructed using the companion object. */ /** Parses input and produces a task to run. Constructed using the companion object. */
sealed trait InputTask[T] { sealed trait InputTask[T]
def mapTask[S](f: Task[T] => Task[S]): InputTask[S]
}
private final class InputStatic[T](val parser: State => Parser[Task[T]]) extends InputTask[T] {
def mapTask[S](f: Task[T] => Task[S]) = new InputStatic(s => parser(s) map f)
}
private sealed trait InputDynamic[T] extends InputTask[T]
{ outer => { outer =>
type Result private[sbt] type Result
def parser: State => Parser[Result] private[sbt] def parser: State => Parser[Result]
def defined: ScopedKey[_] private[sbt] def defined: AttributeKey[Result]
def task: Task[T] private[sbt] def task: Task[T]
def mapTask[S](f: Task[T] => Task[S]) = new InputDynamic[S] {
def mapTask[S](f: Task[T] => Task[S]) = new InputTask[S] {
type Result = outer.Result type Result = outer.Result
def parser = outer.parser def parser = outer.parser
def task = f(outer.task) def task = f(outer.task)
def defined = outer.defined def defined = outer.defined
} }
} }
object InputTask object InputTask
{ {
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0") @deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
@ -35,7 +31,15 @@ object InputTask
def static[I,T](p: Parser[I])(c: I => Task[T]): InputTask[T] = static(p map c) def static[I,T](p: Parser[I])(c: I => Task[T]): InputTask[T] = static(p map c)
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0") @deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
def free[T](p: State => Parser[Task[T]]): InputTask[T] = new InputStatic[T](p) def free[T](p: State => Parser[Task[T]]): InputTask[T] = {
val key = localKey[Task[T]]
new InputTask[T] {
type Result = Task[T]
def parser = p
def defined = key
def task = getResult(key)
}
}
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0") @deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
def free[I,T](p: State => Parser[I])(c: I => Task[T]): InputTask[T] = free(s => p(s) map c) def free[I,T](p: State => Parser[I])(c: I => Task[T]): InputTask[T] = free(s => p(s) map c)
@ -48,27 +52,56 @@ object InputTask
def separate[I,T](p: Initialize[State => Parser[I]])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = 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)) 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.") } @deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
@deprecated("Use the non-overloaded `create` or the `Def.inputTask` macro.", "0.13.0")
def apply[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] = def apply[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
create(p)(action) {
create(p){ it =>
val dummy = TaskKey(localKey[Task[I]])
action(dummy) mapConstant subResultForDummy(dummy)
}
}
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
def apply[I,T](p: State => Parser[I])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
apply(Def.value(p))(action)
// dummy task that will get replaced with the actual mappings from InputTasks to parse results
private[sbt] lazy val inputMap: Task[AttributeMap] = mktask { error("Internal sbt error: input map not substituted.") }
private[this] def getResult[T](key: AttributeKey[Task[T]]): Task[T] = inputMap flatMap { im =>
im get key getOrElse error("Internal sbt error: could not get parser result.")
}
/** The proper solution is to have a Manifest context bound and accept slight source incompatibility,
* The affected InputTask construction methods are all deprecated and so it is better to keep complete
* compatibility. Because the AttributeKey is local, it uses object equality and the manifest is not used. */
private[this] def localKey[T]: AttributeKey[T] = AttributeKey.local[Unit].asInstanceOf[AttributeKey[T]]
private[this] def subResultForDummy[I](dummy: TaskKey[I]) =
new (ScopedKey ~> Option) { def apply[T](sk: ScopedKey[T]) =
if(sk.key eq dummy.key) {
// sk.key: AttributeKey[T], dummy.key: AttributeKey[Task[I]]
// (sk.key eq dummy.key) ==> T == Task[I] because AttributeKey is invariant
Some(getResult(dummy.key).asInstanceOf[T])
} else
None
}
// This interface allows the Parser to be constructed using other Settings, but not Tasks (which is desired). // 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. // 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 Load.finalTransforms and Aggregation.applyDynamicTasks // This requires Aggregation.applyDynamicTasks to inject an AttributeMap with the parse results.
// to handle the dummy task for the parse result. def create[I,T](p: Initialize[State => Parser[I]])(action: Initialize[Task[I]] => Initialize[Task[T]]): Initialize[InputTask[T]] =
// However, this results in a minimal interface to the full capabilities of an InputTask for users
def create[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
{ {
val key: TaskKey[I] = Def.parseResult.asInstanceOf[TaskKey[I]] val key = localKey[I] // TODO: AttributeKey.local[I]
(p zip Def.resolvedScoped zipWith action(key)) { case ((parserF, scoped), act) => val result: Initialize[Task[I]] = (Def.resolvedScoped zipWith Def.valueStrict(InputTask.inputMap)){(scoped, imTask) =>
new InputDynamic[T] imTask map { im => im get key getOrElse error("No parsed value for " + Def.displayFull(scoped) + "\n" + im) }
}
(p zipWith action(result)) { (parserF, act) =>
new InputTask[T]
{ {
type Result = I type Result = I
def parser = parserF def parser = parserF
def task = act def task = act
def defined = scoped def defined = key
} }
} }
} }
@ -81,8 +114,5 @@ object InputTask
/** Implementation detail that is public because it is used y a macro.*/ /** Implementation detail that is public because it is used y a macro.*/
def initParserAsInput[T](i: Initialize[Parser[T]]): Initialize[State => Parser[T]] = i(Types.const) def initParserAsInput[T](i: Initialize[Parser[T]]): Initialize[State => Parser[T]] = i(Types.const)
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
def apply[I,T](p: State => Parser[I])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
apply(Def.value(p))(action)
} }

View File

@ -272,8 +272,9 @@ object TaskMacro
val ttree = t match { case Left(l) => l.tree; case Right(r) => r.tree } val ttree = t match { case Left(l) => l.tree; case Right(r) => r.tree }
val defs = util.collectDefs(ttree, isAnyWrapper) val defs = util.collectDefs(ttree, isAnyWrapper)
val checkQual = util.checkReferences(defs, isAnyWrapper) val checkQual = util.checkReferences(defs, isAnyWrapper)
val unitTask = c.typeOf[TaskKey[Unit]] val unitInitTask = c.typeOf[Initialize[Task[Unit]]]
val taskKeyC = unitTask.typeConstructor val initKeyC = c.typeOf[Initialize[Unit]].typeConstructor
val taskKeyC = c.typeOf[Task[Unit]].typeConstructor
var result: Option[(Tree, Type, ValDef)] = None var result: Option[(Tree, Type, ValDef)] = None
@ -286,8 +287,8 @@ object TaskMacro
else else
{ {
qual.foreach(checkQual) qual.foreach(checkQual)
val keyType = appliedType(taskKeyC, tpe :: Nil) // TaskKey[<tpe>] val itType = appliedType(initKeyC, appliedType(taskKeyC, tpe :: Nil) :: Nil) // Initialize[Task[<tpe>]]
val vd = util.freshValDef(keyType, qual.symbol) // val $x: TaskKey[<tpe>] val vd = util.freshValDef(itType, qual.symbol) // val $x: Initialize[Task[<tpe>]]
result = Some( (qual, tpe, vd) ) result = Some( (qual, tpe, vd) )
val tree = util.refVal(vd) // $x val tree = util.refVal(vd) // $x
tree.setPos(qual.pos) // position needs to be set so that wrapKey passes the position onto the wrapper tree.setPos(qual.pos) // position needs to be set so that wrapKey passes the position onto the wrapper
@ -296,7 +297,7 @@ object TaskMacro
wrapped.tree.setType(tpe) wrapped.tree.setType(tpe)
} }
// Tree for InputTask.create[<tpeA>, <tpeB>](arg1)(arg2) // Tree for InputTask.create[<tpeA>, <tpeB>](arg1)(arg2)
def inputTaskApply(tpeA: Type, tpeB: Type, arg1: Tree, arg2: Tree) = def inputTaskCreate(tpeA: Type, tpeB: Type, arg1: Tree, arg2: Tree) =
{ {
val typedApp = TypeApply(Select(it, InputTaskCreateName), TypeTree(tpeA) :: TypeTree(tpeB) :: Nil) val typedApp = TypeApply(Select(it, InputTaskCreateName), TypeTree(tpeA) :: TypeTree(tpeB) :: Nil)
val app = ApplyTree( ApplyTree(typedApp, arg1 :: Nil), arg2 :: Nil) val app = ApplyTree( ApplyTree(typedApp, arg1 :: Nil), arg2 :: Nil)
@ -313,15 +314,15 @@ object TaskMacro
result match { result match {
case Some((p, tpe, param)) => case Some((p, tpe, param)) =>
val f = Function(param :: Nil, body) val f = Function(param :: Nil, body)
inputTaskApply(tpe, tag.tpe, p, f) inputTaskCreate(tpe, tag.tpe, p, f)
case None => case None =>
// SI-6591 prevents the more direct version using reify: // SI-6591 prevents the more direct version using reify:
// reify { InputTask[Unit,T].create(TaskMacro.emptyParser)(Types.const(body.splice)) } // reify { InputTask[Unit,T].create(TaskMacro.emptyParser)(Types.const(body.splice)) }
val initType = c.weakTypeOf[Initialize[Task[T]]] val initType = c.weakTypeOf[Initialize[Task[T]]]
val tt = Ident(util.singleton(Types)) val tt = Ident(util.singleton(Types))
val f = ApplyTree(TypeApply(Select(tt, "const"), TypeTree(unitTask) :: TypeTree(initType) :: Nil), body :: Nil) val f = ApplyTree(TypeApply(Select(tt, "const"), TypeTree(unitInitTask) :: TypeTree(initType) :: Nil), body :: Nil)
val p = reify { InputTask.emptyParser } val p = reify { InputTask.emptyParser }
inputTaskApply(c.typeOf[Unit], tag.tpe, p.tree, f) inputTaskCreate(c.typeOf[Unit], tag.tpe, p.tree, f)
} }
} }
} }

View File

@ -93,14 +93,19 @@ final object Aggregation
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM)
} }
def applyDynamicTasks[I](s: State, structure: BuildStructure, inputs: Values[InputDynamic[I]], show: Boolean)(implicit display: Show[ScopedKey[_]]): Parser[() => State] = def applyDynamicTasks[I](s: State, structure: BuildStructure, inputs: Values[InputTask[I]], show: Boolean)(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
{ {
val parsers = inputs.map { case KeyValue(k,t) => KeyValue(k, t parser s) } final class Parsed[T](val input: InputTask[_] { type Result = T }, val value: T) {
Command.applyEffect(seqParser(parsers)) { parseds => def addTo(im: AttributeMap): AttributeMap = im.put(input.defined, value)
}
val parsers: Seq[Parser[Parsed[_]]] = for( KeyValue(k,t) <- inputs ) yield
t.parser(s).map( v => new Parsed[t.Result](t, v) )
Command.applyEffect(seq(parsers)) { parseds =>
import EvaluateTask._ import EvaluateTask._
val inputMap = (Map.empty[AnyRef,Any] /: (inputs zip parseds)) { case (im, (id, v)) => im + ((id.value.defined, v.value)) } val inputMap = (AttributeMap.empty /: parseds) { (im, p) => p.addTo(im) }
val dummies = DummyTaskMap(new TaskAndValue(InputTask.inputMap, inputMap) :: Nil) val dummies = DummyTaskMap(new TaskAndValue(InputTask.inputMap, inputMap) :: Nil)
val roots = inputs.map { case KeyValue(k,t) => KeyValue(k,t.task) } val roots = maps(inputs)(_.task)
runTasks(s, structure, roots, dummies, show) runTasks(s, structure, roots, dummies, show)
} }
} }
@ -109,8 +114,7 @@ final object Aggregation
keys.toList match keys.toList match
{ {
case Nil => failure("No such setting/task") case Nil => failure("No such setting/task")
case xs @ KeyValue(_, _: InputStatic[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[InputStatic[t]]])(_.parser(s)), show) case xs @ KeyValue(_, _: InputTask[t]) :: _ => applyDynamicTasks(s, structure, xs.asInstanceOf[Values[InputTask[t]]], 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 @ KeyValue(_, _: Task[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[Task[t]]])(x => success(x)), show)
case xs => success(() => { printSettings(xs, s.log); s} ) case xs => success(() => { printSettings(xs, s.log); s} )
} }

View File

@ -1444,7 +1444,7 @@ trait BuildCommon
{ {
@deprecated("Use Def.inputTask with the `Def.spaceDelimited()` parser.", "0.13.0") @deprecated("Use Def.inputTask with the `Def.spaceDelimited()` parser.", "0.13.0")
def inputTask[T](f: TaskKey[Seq[String]] => Initialize[Task[T]]): Initialize[InputTask[T]] = def inputTask[T](f: TaskKey[Seq[String]] => Initialize[Task[T]]): Initialize[InputTask[T]] =
InputTask.create(Def.value((s: State) => Def.spaceDelimited()))(f) InputTask.apply(Def.value((s: State) => Def.spaceDelimited()))(f)
implicit def globFilter(expression: String): NameFilter = GlobFilter(expression) implicit def globFilter(expression: String): NameFilter = GlobFilter(expression)
implicit def richAttributed(s: Seq[Attributed[File]]): RichAttributed = new RichAttributed(s) implicit def richAttributed(s: Seq[Attributed[File]]): RichAttributed = new RichAttributed(s)

View File

@ -14,7 +14,7 @@ package sbt
import Compiler.{Compilers,Inputs} import Compiler.{Compilers,Inputs}
import inc.{FileValueCache, Locate} import inc.{FileValueCache, Locate}
import Project.{inScope,makeSettings} import Project.{inScope,makeSettings}
import Def.{parseResult, ScopedKey, ScopeLocal, Setting} import Def.{ScopedKey, ScopeLocal, Setting}
import Keys.{appConfiguration, baseDirectory, configuration, fullResolvers, fullClasspath, pluginData, streams, thisProject, thisProjectRef, update} import Keys.{appConfiguration, baseDirectory, configuration, fullResolvers, fullClasspath, pluginData, streams, thisProject, thisProjectRef, update}
import Keys.{exportedProducts, isDummy, loadedBuild, resolvedScoped, taskDefinitionKey} import Keys.{exportedProducts, isDummy, loadedBuild, resolvedScoped, taskDefinitionKey}
import tools.nsc.reporters.ConsoleReporter import tools.nsc.reporters.ConsoleReporter
@ -136,7 +136,6 @@ object Load
// 1. the scope of 'streams' is the same as the defining key and has the task axis set to the defining key // 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 // 2. the defining key is stored on constructed tasks
// 3. resolvedScoped is replaced with the defining key as a value // 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. // Note: this must be idempotent.
def finalTransforms(ss: Seq[Setting[_]]): Seq[Setting[_]] = def finalTransforms(ss: Seq[Setting[_]]): Seq[Setting[_]] =
{ {
@ -154,10 +153,6 @@ object Load
key.key match key.key match
{ {
case resolvedScoped.key => Some(defining.asInstanceOf[T]) 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 " + Def.displayFull(defining) + "\n" + m) }
Some(getResult.asInstanceOf[T])
case _ => None case _ => None
} }
} }