Merge pull request #5260 from eatkins/state-transform

Improve StateTransform
This commit is contained in:
Ethan Atkins 2019-12-02 08:51:18 -08:00 committed by GitHub
commit 21f09e6025
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 93 additions and 22 deletions

View File

@ -515,17 +515,14 @@ object EvaluateTask {
state: State,
root: Task[T]
): (State, Result[T]) = {
val newState = results(root) match {
case Value(KeyValue(_, st: StateTransform) :: Nil) => st.state
case _ => stateTransform(results)(state)
}
(newState, results(root))
(stateTransform(results)(state), results(root))
}
def stateTransform(results: RMap[Task, Result]): State => State =
Function.chain(
results.toTypedSeq flatMap {
case results.TPair(Task(info, _), Value(v)) => info.post(v) get transformState
case _ => Nil
case results.TPair(_, Value(KeyValue(_, st: StateTransform))) => Some(st.transform)
case results.TPair(Task(info, _), Value(v)) => info.post(v) get transformState
case _ => Nil
}
)

View File

@ -7,14 +7,43 @@
package sbt
final class StateTransform(val state: State) {
override def equals(o: Any): Boolean = o match {
case that: StateTransform => this.state == that.state
case _ => false
}
override def hashCode: Int = state.hashCode
override def toString: String = s"StateTransform($state)"
/**
* Provides a mechanism for a task to transform the state based on the result of the task. For
* example:
* {{{
* val foo = AttributeKey[String]("foo")
* val setFoo = taskKey[StateTransform]("")
* setFoo := {
* state.value.get(foo) match {
* case None => StateTransform(_.put(foo, "foo"))
* case _ => StateTransform(identity)
* }
* }
* val getFoo = taskKey[Option[String]]
* getFoo := state.value.get(foo)
* }}}
* Prior to a call to `setFoo`, `getFoo` will return `None`. After a call to `setFoo`, `getFoo` will
* return `Some("foo")`.
*/
final class StateTransform private (val transform: State => State, stateProxy: () => State) {
@deprecated("Exists only for binary compatibility with 1.3.x.", "1.4.0")
private[sbt] def state: State = stateProxy()
@deprecated("1.4.0", "Use the constructor that takes a transform function.")
private[sbt] def this(state: State) = this((_: State) => state, () => state)
}
object StateTransform {
@deprecated("Exists only for binary compatibility with 1.3.x", "1.4.0")
def apply(state: State): State = state
/**
* Create an instance of [[StateTransform]].
* @param transform the transformation to apply after task evaluation has completed
* @return the [[StateTransform]].
*/
def apply(transform: State => State) =
new StateTransform(
transform,
() => throw new IllegalStateException("No state was added to the StateTransform.")
)
}

View File

@ -117,7 +117,8 @@ private[sbt] object Continuous extends DeprecatedContinuous {
private[sbt] def continuousTask: Def.Initialize[InputTask[StateTransform]] =
Def.inputTask {
val (initialCount, commands) = continuousParser.parsed
new StateTransform(runToTermination(state.value, commands, initialCount, isCommand = false))
val newState = runToTermination(state.value, commands, initialCount, isCommand = false)
StateTransform(_ => newState)
}
private[sbt] val dynamicInputs = taskKey[Option[mutable.Set[DynamicInput]]](

View File

@ -24,7 +24,7 @@ private[sbt] object CheckBuildSources {
val st: State = state.value
val firstTime = st.get(hasCheckedMetaBuild).fold(true)(_.compareAndSet(false, true))
(onChangedBuildSource in Scope.Global).value match {
case IgnoreSourceChanges => new StateTransform(st)
case IgnoreSourceChanges => StateTransform(identity)
case o =>
import sbt.nio.FileStamp.Formats._
logger.debug("Checking for meta build source updates")
@ -47,16 +47,16 @@ private[sbt] object CheckBuildSources {
logger.info(s"$prefix\nReloading sbt...")
val remaining =
Exec("reload", None, None) :: st.currentCommand.toList ::: st.remainingCommands
new StateTransform(st.copy(currentCommand = None, remainingCommands = remaining))
StateTransform(_.copy(currentCommand = None, remainingCommands = remaining))
} else {
val tail = "Apply these changes by running `reload`.\nAutomatically reload the " +
"build when source changes are detected by setting " +
"`Global / onChangedBuildSource := ReloadOnSourceChanges`.\nDisable this " +
"warning by setting `Global / onChangedBuildSource := IgnoreSourceChanges`."
logger.warn(s"$prefix\n$tail")
new StateTransform(st)
StateTransform(identity)
}
case _ => new StateTransform(st)
case _ => StateTransform(identity)
}
}
}

View File

@ -0,0 +1,31 @@
import complete.DefaultParsers._
import complete.Parser
import sbt.Def.Initialize
val keep = taskKey[Int]("")
val checkKeep = inputKey[Unit]("")
keep := (getPrevious(keep) map { case None => 3; case Some(x) => x + 1 } keepAs keep).value
checkKeep := inputCheck((ctx, s) => Space ~> str(getFromContext(keep, ctx, s))).evaluated
val foo = AttributeKey[Int]("foo")
val transform = taskKey[StateTransform]("")
transform := {
keep.value
StateTransform(_.put(foo, 1))
}
def inputCheck[T](f: (ScopedKey[_], State) => Parser[T]): Initialize[InputTask[Unit]] =
InputTask(resolvedScoped(ctx => (s: State) => f(ctx, s)))(dummyTask)
val checkTransform = taskKey[Unit]("")
checkTransform := {
assert(state.value.get(foo).contains(1))
}
def dummyTask: Any => Initialize[Task[Unit]] =
(_: Any) =>
maxErrors map { _ =>
()
}
def str(o: Option[Int]) = o match { case None => "blue"; case Some(i) => i.toString }

View File

@ -0,0 +1,13 @@
> checkKeep blue
-> checkTransform
> transform
> checkKeep 3
> checkTransform
> transform
> checkKeep 4

View File

@ -38,8 +38,8 @@ object Build {
checkStringValue := checkStringValueImpl.evaluated,
watchOnFileInputEvent := { (_, _) => Watch.CancelWatch },
watchTasks := Def.inputTask {
val prev = watchTasks.evaluated
new StateTransform(prev.state.fail)
watchTasks.evaluated
StateTransform(_.fail)
}.evaluated
)
}

View File

@ -25,7 +25,7 @@ object Build {
watchOnFileInputEvent := { (_, _) => Watch.CancelWatch },
watchTasks := Def.inputTask {
val prev = watchTasks.evaluated
new StateTransform(prev.state.fail)
StateTransform(_.fail)
}.evaluated
)
}