mirror of https://github.com/sbt/sbt.git
more refactoring of special settings: input tasks. fixes #114
This commit is contained in:
parent
fb9e3bd516
commit
4ae0ba6b57
|
|
@ -6,7 +6,7 @@ package sbt
|
||||||
import CommandSupport.logger
|
import CommandSupport.logger
|
||||||
import Project.ScopedKey
|
import Project.ScopedKey
|
||||||
import Load.BuildStructure
|
import Load.BuildStructure
|
||||||
import Keys.{aggregate, parseResult, showSuccess, showTiming, timingFormat}
|
import Keys.{aggregate, showSuccess, showTiming, timingFormat}
|
||||||
import sbt.complete.Parser
|
import sbt.complete.Parser
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import Parser._
|
import Parser._
|
||||||
|
|
@ -129,29 +129,18 @@ final object Aggregation
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class Dummies[HL <: HList](tasks: KList[Task,HL], values: HL)
|
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] =
|
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) }
|
val parsers = inputs.map { case KeyValue(k,t) => KeyValue(k, t parser s) }
|
||||||
Command.applyEffect(seqParser(parsers)) { parseds =>
|
Command.applyEffect(seqParser(parsers)) { parseds =>
|
||||||
import EvaluateTask._
|
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) }
|
val roots = inputs.map { case KeyValue(k,t) => KeyValue(k,t.task) }
|
||||||
runTasks(s, structure, roots, dummies, show)
|
runTasks(s, structure, roots, dummies, show)
|
||||||
s
|
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] =
|
def valueParser(s: State, structure: BuildStructure, show: Boolean)(key: ScopedKey[_]): Parser[() => State] =
|
||||||
getTasks(key, structure, true).toList match
|
getTasks(key, structure, true).toList match
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ package sbt
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import Project.{ScopedKey, Setting}
|
import Project.{ScopedKey, Setting}
|
||||||
import Keys.{streams, Streams, TaskStreams}
|
import Keys.{streams, Streams, TaskStreams}
|
||||||
import Keys.{dummyState, dummyStreamsManager, parseResult, streamsManager, taskDefinitionKey}
|
import Keys.{dummyState, dummyStreamsManager, streamsManager, taskDefinitionKey}
|
||||||
import Scope.{GlobalScope, ThisScope}
|
import Scope.{GlobalScope, ThisScope}
|
||||||
import scala.Console.{RED, RESET}
|
import scala.Console.{RED, RESET}
|
||||||
|
|
||||||
|
|
@ -131,8 +131,6 @@ object EvaluateTask
|
||||||
stream.open()
|
stream.open()
|
||||||
stream
|
stream
|
||||||
})
|
})
|
||||||
else if(scoped.key == parseResult.key)
|
|
||||||
Seq(parseResult in scoped.scope := error("Unsubstituted parse result for " + Project.display(scoped)) )
|
|
||||||
else
|
else
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ object Keys
|
||||||
val (state, dummyState) = dummy[State]("state", "Current build state.")
|
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 (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.")
|
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 triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by")
|
||||||
val runBefore = AttributeKey[Seq[Task[_]]]("run-before")
|
val runBefore = AttributeKey[Seq[Task[_]]]("run-before")
|
||||||
|
|
|
||||||
|
|
@ -123,17 +123,17 @@ object Load
|
||||||
(rootEval, new BuildStructure(projects, loaded.root, settings, data, index, streams, delegates, config.scopeLocal))
|
(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
|
// map dependencies on the special tasks:
|
||||||
// additionally, set the task axis to the defining key if it is not set
|
// 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 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]) =
|
def mapSpecial(to: ScopedKey[_]) = new (ScopedKey ~> ScopedKey){ def apply[T](key: ScopedKey[T]) =
|
||||||
if(isSpecial(key.key))
|
if(key.key == streams.key)
|
||||||
{
|
ScopedKey(Scope.fillTaskAxis(Scope.replaceThis(to.scope)(key.scope), to.key), key.key)
|
||||||
val replaced = Scope.replaceThis(to.scope)(key.scope)
|
|
||||||
ScopedKey(Scope.fillTaskAxis(replaced, to.key), key.key)
|
|
||||||
}
|
|
||||||
else key
|
else key
|
||||||
}
|
}
|
||||||
def setDefining[T] = (key: ScopedKey[T], value: T) => value match {
|
def setDefining[T] = (key: ScopedKey[T], value: T) => value match {
|
||||||
|
|
@ -142,9 +142,17 @@ object Load
|
||||||
case _ => value
|
case _ => value
|
||||||
}
|
}
|
||||||
def setResolved(defining: ScopedKey[_]) = new (ScopedKey ~> Option) { def apply[T](key: ScopedKey[T]): Option[T] =
|
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] =
|
def setDefinitionKey[T](tk: Task[T], key: ScopedKey[_]): Task[T] =
|
||||||
if(isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)
|
if(isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,13 @@ private sealed trait InputDynamic[T] extends InputTask[T]
|
||||||
{ outer =>
|
{ outer =>
|
||||||
type Result
|
type Result
|
||||||
def parser: State => Parser[Result]
|
def parser: State => Parser[Result]
|
||||||
|
def defined: ScopedKey[_]
|
||||||
def task: Task[T]
|
def task: Task[T]
|
||||||
def mapTask[S](f: Task[T] => Task[S]) = new InputDynamic[S] {
|
def mapTask[S](f: Task[T] => Task[S]) = new InputDynamic[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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
object InputTask
|
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]] =
|
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.") }
|
||||||
|
|
||||||
// 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 injectStreams and Act to handle the dummy task for the parse result.
|
// This is the ugly part, requiring hooks in Load.finalTransforms and Aggregation.applyDynamicTasks
|
||||||
// However, this is results in a minimal interface to the full capabilities of an InputTask for users
|
// 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]] =
|
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]]
|
val key: TaskKey[I] = Keys.parseResult.asInstanceOf[TaskKey[I]]
|
||||||
p.zipWith(action(key)) { (parserF, act) =>
|
(p zip Keys.resolvedScoped.identity zipWith action(key)) { case ((parserF, scoped), act) =>
|
||||||
new InputDynamic[T]
|
new InputDynamic[T]
|
||||||
{
|
{
|
||||||
type Result = I
|
type Result = I
|
||||||
def parser = parserF
|
def parser = parserF
|
||||||
def task = act
|
def task = act
|
||||||
|
def defined = scoped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue