mirror of https://github.com/sbt/sbt.git
Task state.
* Allow tasks to provide State transformations that are applied after all tasks complete. * Provide convenience methods for preserving state across invocations. * Option of session or persisted state.
This commit is contained in:
parent
97028cb7f8
commit
5918c24722
|
|
@ -72,9 +72,8 @@ final object Aggregation
|
|||
def applyTasks[T](s: State, structure: BuildStructure, ps: Values[Parser[Task[T]]], show: Boolean)(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
|
||||
Command.applyEffect(seqParser(ps)) { ts =>
|
||||
runTasks(s, structure, ts, Dummies(KNil, HNil), show)
|
||||
s
|
||||
}
|
||||
def runTasks[HL <: HList, T](s: State, structure: Load.BuildStructure, ts: Values[Task[T]], extra: Dummies[HL], show: Boolean)(implicit display: Show[ScopedKey[_]])
|
||||
def runTasks[HL <: HList, T](s: State, structure: Load.BuildStructure, ts: Values[Task[T]], extra: Dummies[HL], show: Boolean)(implicit display: Show[ScopedKey[_]]): State =
|
||||
{
|
||||
import EvaluateTask._
|
||||
import std.TaskExtra._
|
||||
|
|
@ -82,13 +81,15 @@ final object Aggregation
|
|||
val toRun = ts map { case KeyValue(k,t) => t.map(v => KeyValue(k,v)) } join;
|
||||
val workers = maxWorkers(extracted, structure)
|
||||
val start = System.currentTimeMillis
|
||||
val result = withStreams(structure){ str => runTask(toRun, str, structure.index.triggers, maxWorkers = workers)(nodeView(s, str, extra.tasks, extra.values)) }
|
||||
val (newS, result) = withStreams(structure){ str => runTask(toRun, s,str, structure.index.triggers, maxWorkers = workers)(nodeView(s, str, extra.tasks, extra.values)) }
|
||||
val stop = System.currentTimeMillis
|
||||
val log = logger(s)
|
||||
val log = logger(newS)
|
||||
|
||||
val success = result match { case Value(_) => true; case Inc(_) => false }
|
||||
try { onResult(result, log) { results => if(show) printSettings(results, log) } }
|
||||
finally { printSuccess(start, stop, extracted, success, log) }
|
||||
|
||||
newS
|
||||
}
|
||||
def maxWorkers(extracted: Extracted, structure: Load.BuildStructure): Int =
|
||||
(Keys.parallelExecution in extracted.currentRef get structure.data) match {
|
||||
|
|
@ -138,7 +139,6 @@ final object Aggregation
|
|||
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
|
||||
}
|
||||
}
|
||||
def valueParser(s: State, structure: BuildStructure, show: Boolean)(key: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
|
||||
|
|
|
|||
|
|
@ -1125,4 +1125,18 @@ trait BuildCommon
|
|||
val newConfigs = cs filter { c => !existingName(c.name) }
|
||||
overridden ++ newConfigs
|
||||
}
|
||||
|
||||
// these are intended for use in input tasks for creating parsers
|
||||
def getFromContext[T](task: ScopedTask[T], context: ScopedKey[_], s: State): Option[T] =
|
||||
SessionVar.get(SessionVar.resolveContext(task.scopedKey, context.scope, s), s)
|
||||
|
||||
def loadFromContext[T](task: ScopedTask[T], context: ScopedKey[_], s: State)(implicit f: sbinary.Format[T]): Option[T] =
|
||||
SessionVar.load(SessionVar.resolveContext(task.scopedKey, context.scope, s), s)
|
||||
|
||||
// these are for use in tasks
|
||||
def loadPrevious[T](task: ScopedTask[T])(implicit f: sbinary.Format[T]): Initialize[Task[Option[T]]] =
|
||||
(state, resolvedScoped) map { (s, ctx) => loadFromContext(task, ctx, s)(f) }
|
||||
|
||||
def getPrevious[T](task: ScopedTask[T]): Initialize[Task[Option[T]]] =
|
||||
(state, resolvedScoped) map { (s, ctx) => getFromContext(task, ctx, s) }
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ package sbt
|
|||
import java.io.File
|
||||
import Project.{ScopedKey, Setting}
|
||||
import Keys.{globalLogging, streams, Streams, TaskStreams}
|
||||
import Keys.{dummyState, dummyStreamsManager, streamsManager, taskDefinitionKey}
|
||||
import Keys.{dummyState, dummyStreamsManager, streamsManager, taskDefinitionKey, transformState}
|
||||
import Scope.{GlobalScope, ThisScope}
|
||||
import scala.Console.{RED, RESET}
|
||||
|
||||
|
|
@ -29,14 +29,19 @@ object EvaluateTask
|
|||
{
|
||||
val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root))
|
||||
val pluginKey = Keys.fullClasspath in Configurations.Runtime
|
||||
val evaluated = evaluateTask(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root)
|
||||
val result = evaluated getOrElse error("Plugin classpath does not exist for plugin definition at " + pluginDef.root)
|
||||
val evaluated = apply(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root)
|
||||
val (newS, result) = evaluated getOrElse error("Plugin classpath does not exist for plugin definition at " + pluginDef.root)
|
||||
Project.runUnloadHooks(newS) // discard state
|
||||
processResult(result, log)
|
||||
}
|
||||
|
||||
@deprecated("This method does not apply state changes requested during task execution. Use 'apply' instead, which does.", "0.11.1")
|
||||
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
|
||||
apply(structure, taskKey, state, ref, checkCycles, maxWorkers).map(_._2)
|
||||
def apply[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[(State, Result[T])] =
|
||||
withStreams(structure) { str =>
|
||||
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
|
||||
runTask(task, str, structure.index.triggers, checkCycles, maxWorkers)(toNode)
|
||||
runTask(task, state, str, structure.index.triggers, checkCycles, maxWorkers)(toNode)
|
||||
}
|
||||
def logIncResult(result: Result[_], streams: Streams) = result match { case Inc(i) => logIncomplete(i, streams); case _ => () }
|
||||
def logIncomplete(result: Incomplete, streams: Streams)
|
||||
|
|
@ -73,16 +78,30 @@ object EvaluateTask
|
|||
def nodeView[HL <: HList](state: State, streams: Streams, extraDummies: KList[Task, HL] = KNil, extraValues: HL = HNil): Execute.NodeView[Task] =
|
||||
Transform(dummyStreamsManager :^: KCons(dummyState, extraDummies), streams :+: HCons(state, extraValues))
|
||||
|
||||
def runTask[Task[_] <: AnyRef, T](root: Task[T], streams: Streams, triggers: Triggers[Task], checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
|
||||
def runTask[T](root: Task[T], state: State, streams: Streams, triggers: Triggers[Task], checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): (State, Result[T]) =
|
||||
{
|
||||
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
|
||||
|
||||
val x = new Execute[Task](checkCycles, triggers)(taskToNode)
|
||||
val result = try { x.run(root)(service) } finally { shutdown() }
|
||||
val (newState, result) =
|
||||
try applyResults(x.runKeep(root)(service), state, root)
|
||||
catch { case inc: Incomplete => (state, Inc(inc)) }
|
||||
finally shutdown()
|
||||
val replaced = transformInc(result)
|
||||
logIncResult(replaced, streams)
|
||||
replaced
|
||||
(newState, replaced)
|
||||
}
|
||||
|
||||
def applyResults[T](results: RMap[Task, Result], state: State, root: Task[T]): (State, Result[T]) =
|
||||
(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
|
||||
}
|
||||
)
|
||||
|
||||
def transformInc[T](result: Result[T]): Result[T] =
|
||||
// taskToKey needs to be before liftAnonymous. liftA only lifts non-keyed (anonymous) Incompletes.
|
||||
result.toEither.left.map { i => Incomplete.transformBU(i)(convertCyclicInc andThen taskToKey andThen liftAnonymous ) }
|
||||
|
|
|
|||
|
|
@ -32,10 +32,11 @@ object GlobalPlugin
|
|||
def load(base: File, s: State, config: LoadBuildConfiguration): GlobalPlugin =
|
||||
{
|
||||
val (structure, state) = build(base, s, config)
|
||||
val data = extract(state, structure)
|
||||
val (newS, data) = extract(state, structure)
|
||||
Project.runUnloadHooks(newS) // discard state
|
||||
GlobalPlugin(data, structure, inject(data), base)
|
||||
}
|
||||
def extract(state: State, structure: BuildStructure): GlobalPluginData =
|
||||
def extract(state: State, structure: BuildStructure): (State, GlobalPluginData) =
|
||||
{
|
||||
import structure.{data, root, rootProject}
|
||||
val p: Scope = Scope.GlobalScope in ProjectRef(root, rootProject(root))
|
||||
|
|
@ -47,12 +48,13 @@ object GlobalPlugin
|
|||
val task = taskInit mapReferenced Project.mapScope(Scope replaceThis p) evaluate data
|
||||
evaluate(state, structure, task)
|
||||
}
|
||||
def evaluate[T](state: State, structure: BuildStructure, t: Task[T]): T =
|
||||
def evaluate[T](state: State, structure: BuildStructure, t: Task[T]): (State, T) =
|
||||
{
|
||||
import EvaluateTask._
|
||||
withStreams(structure) { str =>
|
||||
val nv = nodeView(state, str)
|
||||
processResult(runTask(t, str, structure.index.triggers)(nv), log(state))
|
||||
val (newS, result) = runTask(t, state, str, structure.index.triggers)(nv)
|
||||
(newS, processResult(result, log(newS)))
|
||||
}
|
||||
}
|
||||
private[this] def log(s: State) = CommandSupport.logger(s)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ package sbt
|
|||
import descriptor.ModuleDescriptor, id.ModuleRevisionId
|
||||
import org.scalatools.testing.Framework
|
||||
import Configurations.CompilerPlugin
|
||||
import Types.Id
|
||||
|
||||
object Keys
|
||||
{
|
||||
|
|
@ -47,6 +48,7 @@ object Keys
|
|||
val onLoad = SettingKey[State => State]("on-load", "Transformation to apply to the build state when the build is loaded.")
|
||||
val onUnload = SettingKey[State => State]("on-unload", "Transformation to apply to the build state when the build is unloaded.")
|
||||
val onLoadMessage = SettingKey[String]("on-load-message", "Message to display when the project is loaded.")
|
||||
val transformState = AttributeKey[State => State]("transform-state", "State transformation to apply after tasks run.")
|
||||
|
||||
val onComplete = SettingKey[() => Unit]("on-complete", "Hook to run when task evaluation completes. The type of this setting is subject to change, pending the resolution of SI-2915.")
|
||||
// https://issues.scala-lang.org/browse/SI-2915
|
||||
|
|
@ -291,6 +293,7 @@ object Keys
|
|||
val skip = TaskKey[Boolean]("skip", "For tasks that support it (currently only 'compile'), setting skip to true will force the task to not to do its work. This exact semantics may vary by task.")
|
||||
|
||||
// special
|
||||
val sessionVars = AttributeKey[SessionVar.Map]("session-vars", "Bindings that exist for the duration of the session.")
|
||||
val parallelExecution = SettingKey[Boolean]("parallel-execution", "Enables (true) or disables (false) parallel execution of tasks.")
|
||||
val settings = TaskKey[Settings[Scope]]("settings", "Provides access to the project data for the build.")
|
||||
val streams = TaskKey[TaskStreams]("streams", "Provides streams for logging and persisting data.")
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ package sbt
|
|||
import java.io.File
|
||||
import java.net.URI
|
||||
import Project._
|
||||
import Keys.{appConfiguration, stateBuildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, streams, thisProject, thisProjectRef, watch}
|
||||
import Keys.{appConfiguration, stateBuildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, sessionVars, shellPrompt, thisProject, thisProjectRef, watch}
|
||||
import Scope.{GlobalScope,ThisScope}
|
||||
import Load.BuildStructure
|
||||
import CommandSupport.logger
|
||||
import Types.idFun
|
||||
import Types.{idFun, Id}
|
||||
|
||||
sealed trait ProjectDefinition[PR <: ProjectReference]
|
||||
{
|
||||
|
|
@ -71,13 +71,15 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings,
|
|||
def get[T](key: ScopedSetting[T]) = getOrError(inCurrent(key), key.key)
|
||||
def getOpt[T](key: ScopedSetting[T]): Option[T] = structure.data.get(inCurrent(key), key.key)
|
||||
private[this] def inCurrent[T](key: ScopedSetting[T]): Scope = if(key.scope.project == This) key.scope.copy(project = Select(currentRef)) else key.scope
|
||||
def evalTask[T](key: ScopedTask[T], state: State): T =
|
||||
@deprecated("This method does not apply state changes requested during task execution. Use 'runTask' instead, which does.", "0.11.1")
|
||||
def evalTask[T](key: ScopedTask[T], state: State): T = runTask(key, state)._2
|
||||
def runTask[T](key: ScopedTask[T], state: State): (State, T) =
|
||||
{
|
||||
import EvaluateTask._
|
||||
val rkey = Project.mapScope(Scope.resolveScope(GlobalScope, currentRef.build, rootProject) )( key.scopedKey )
|
||||
val value: Option[Result[T]] = evaluateTask(structure, key.task.scopedKey, state, currentRef)
|
||||
val result = getOrError(rkey.scope, rkey.key, value)
|
||||
processResult(result, ConsoleLogger())
|
||||
val value: Option[(State, Result[T])] = apply(structure, key.task.scopedKey, state, currentRef)
|
||||
val (newS, result) = getOrError(rkey.scope, rkey.key, value)
|
||||
(newS, processResult(result, logger(newS)))
|
||||
}
|
||||
private def getOrError[T](scope: Scope, key: AttributeKey[_], value: Option[T])(implicit display: Show[ScopedKey[_]]): T =
|
||||
value getOrElse error(display(ScopedKey(scope, key)) + " is undefined.")
|
||||
|
|
@ -153,12 +155,16 @@ object Project extends Init[Scope] with ProjectExtra
|
|||
def getProject(ref: ProjectRef, units: Map[URI, Load.LoadedBuildUnit]): Option[ResolvedProject] =
|
||||
(units get ref.build).flatMap(_.defined get ref.project)
|
||||
|
||||
def setProject(session: SessionSettings, structure: BuildStructure, s: State): State =
|
||||
def runUnloadHooks(s: State): State =
|
||||
{
|
||||
val previousOnUnload = orIdentity(s get Keys.onUnload.key)
|
||||
val unloaded = previousOnUnload(s.runExitHooks())
|
||||
previousOnUnload(s.runExitHooks())
|
||||
}
|
||||
def setProject(session: SessionSettings, structure: BuildStructure, s: State): State =
|
||||
{
|
||||
val unloaded = runUnloadHooks(s)
|
||||
val (onLoad, onUnload) = getHooks(structure.data)
|
||||
val newAttrs = unloaded.attributes.put(stateBuildStructure, structure).put(sessionSettings, session).put(Keys.onUnload.key, onUnload)
|
||||
val newAttrs = unloaded.attributes.put(stateBuildStructure, structure).put(sessionSettings, session).put(Keys.onUnload.key, onUnload).put(sessionVars, SessionVar.emptyMap)
|
||||
val newState = unloaded.copy(attributes = newAttrs)
|
||||
onLoad(updateCurrent( newState ))
|
||||
}
|
||||
|
|
@ -323,11 +329,13 @@ object Project extends Init[Scope] with ProjectExtra
|
|||
val newS = setProjectReturn(s, newBase :: projectReturn(s))
|
||||
(newS, newBase)
|
||||
}
|
||||
|
||||
@deprecated("This method does not apply state changes requested during task execution. Use 'runTask' instead, which does.", "0.11.1")
|
||||
def evaluateTask[T](taskKey: ScopedKey[Task[T]], state: State, checkCycles: Boolean = false, maxWorkers: Int = EvaluateTask.SystemProcessors): Option[Result[T]] =
|
||||
runTask(taskKey, state, checkCycles, maxWorkers).map(_._2)
|
||||
def runTask[T](taskKey: ScopedKey[Task[T]], state: State, checkCycles: Boolean = false, maxWorkers: Int = EvaluateTask.SystemProcessors): Option[(State, Result[T])] =
|
||||
{
|
||||
val extracted = Project.extract(state)
|
||||
EvaluateTask.evaluateTask(extracted.structure, taskKey, state, extracted.currentRef, checkCycles, maxWorkers)
|
||||
EvaluateTask(extracted.structure, taskKey, state, extracted.currentRef, checkCycles, maxWorkers)
|
||||
}
|
||||
// this is here instead of Scoped so that it is considered without need for import (because of Project.Initialize)
|
||||
implicit def richInitializeTask[T](init: Initialize[Task[T]]): Scoped.RichInitializeTask[T] = new Scoped.RichInitializeTask(init)
|
||||
|
|
@ -345,4 +353,66 @@ trait ProjectExtra
|
|||
inScope(ThisScope.copy(task = Select(t.key)) )( ss )
|
||||
def inScope(scope: Scope)(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
Project.transform(Scope.replaceThis(scope), ss)
|
||||
}
|
||||
|
||||
import sbinary.{Format, Operations}
|
||||
object SessionVar
|
||||
{
|
||||
// these are required because of inference+manifest limitations
|
||||
final case class Key[T](key: ScopedKey[Task[T]])
|
||||
final case class Map(map: IMap[Key, Id]) {
|
||||
def get[T](k: ScopedKey[Task[T]]): Option[T] = map get Key(k)
|
||||
def put[T](k: ScopedKey[Task[T]], v: T): Map = Map(map put (Key(k), v))
|
||||
}
|
||||
def emptyMap = Map(IMap.empty)
|
||||
|
||||
def persistAndSet[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: sbinary.Format[T]): State =
|
||||
{
|
||||
persist(key, state, value)(f)
|
||||
set(key, state, value)
|
||||
}
|
||||
|
||||
def persist[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: sbinary.Format[T]): Unit =
|
||||
Project.structure(state).streams.use(key)( s =>
|
||||
Operations.write(s.binary(TaskData.DefaultDataID), value)(f)
|
||||
)
|
||||
|
||||
def get[T](key: ScopedKey[Task[T]], state: State): Option[T] = orEmpty(state get sessionVars) get key
|
||||
|
||||
def set[T](key: ScopedKey[Task[T]], state: State, value: T): State = state.update(sessionVars)(om => orEmpty(om) put (key, value))
|
||||
|
||||
def orEmpty(opt: Option[Map]) = opt getOrElse emptyMap
|
||||
|
||||
def transform[S](task: Task[S], f: (State, S) => State): Task[S] =
|
||||
{
|
||||
val g = (s: S, map: AttributeMap) => map.put(Keys.transformState, (state: State) => f(state, s))
|
||||
task.copy(info = task.info.postTransform(g))
|
||||
}
|
||||
|
||||
def resolveContext[T](key: ScopedKey[Task[T]], context: Scope, state: State): ScopedKey[Task[T]] =
|
||||
{
|
||||
val subScope = Scope.replaceThis(context)(key.scope)
|
||||
val scope = Project.structure(state).data.definingScope(subScope, key.key) getOrElse subScope
|
||||
ScopedKey(scope, key.key)
|
||||
}
|
||||
|
||||
def read[T](key: ScopedKey[Task[T]], state: State)(implicit f: Format[T]): Option[T] =
|
||||
Project.structure(state).streams.use(key) { s =>
|
||||
try { Some(Operations.read(s.readBinary(key, TaskData.DefaultDataID))) }
|
||||
catch { case e: Exception => None }
|
||||
}
|
||||
|
||||
def load[T](key: ScopedKey[Task[T]], state: State)(implicit f: Format[T]): Option[T] =
|
||||
get(key, state) orElse read(key, state)(f)
|
||||
|
||||
def loadAndSet[T](key: ScopedKey[Task[T]], state: State, setIfUnset: Boolean = true)(implicit f: Format[T]): (State, Option[T]) =
|
||||
get(key, state) match {
|
||||
case s: Some[T] => (state, s)
|
||||
case None => read(key, state)(f) match {
|
||||
case s @ Some(t) =>
|
||||
val newState = if(setIfUnset && get(key, state).isDefined) state else set(key, state, t)
|
||||
(newState, s)
|
||||
case None => (state, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -166,4 +166,4 @@ save, save-all
|
|||
case c: Clear => if(c.all) clearAllSettings(s) else clearSettings(s)
|
||||
case r: Remove => removeSettings(s,r.ranges)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ trait StateOps {
|
|||
def get[T](key: AttributeKey[T]): Option[T]
|
||||
def put[T](key: AttributeKey[T], value: T): State
|
||||
def remove(key: AttributeKey[_]): State
|
||||
def update[T](key: AttributeKey[T])(f: Option[T] => T): State
|
||||
def has(key: AttributeKey[_]): Boolean
|
||||
def baseDir: File
|
||||
def locked[T](file: File)(t: => T): T
|
||||
|
|
@ -73,6 +74,7 @@ object State
|
|||
def exit(ok: Boolean) = setResult(Some(Exit(if(ok) 0 else 1)))
|
||||
def get[T](key: AttributeKey[T]) = s.attributes get key
|
||||
def put[T](key: AttributeKey[T], value: T) = s.copy(attributes = s.attributes.put(key, value))
|
||||
def update[T](key: AttributeKey[T])(f: Option[T] => T): State = put(key, f(get(key)))
|
||||
def has(key: AttributeKey[_]) = s.attributes contains key
|
||||
def remove(key: AttributeKey[_]) = s.copy(attributes = s.attributes remove key)
|
||||
def fail =
|
||||
|
|
|
|||
|
|
@ -190,6 +190,15 @@ object Scoped
|
|||
|
||||
def dependsOn(tasks: AnyInitTask*): Initialize[Task[S]] = (i, Initialize.joinAny(tasks)) { (thisTask, deps) => thisTask.dependsOn(deps : _*) }
|
||||
|
||||
import SessionVar.{persistAndSet, resolveContext, set, transform}
|
||||
|
||||
def updateState(f: (State, S) => State): Initialize[Task[S]] = i(t => transform(t, f))
|
||||
def storeAs(key: ScopedTask[S])(implicit f: sbinary.Format[S]): Initialize[Task[S]] = (Keys.resolvedScoped, i) { (scoped, task) =>
|
||||
transform(task, (state, value) => persistAndSet( resolveContext(key, scoped.scope, state), state, value)(f))
|
||||
}
|
||||
def keepAs(key: ScopedTask[S]): Initialize[Task[S]] =
|
||||
(i, Keys.resolvedScoped)( (t,scoped) => transform(t, (state,value) => set(resolveContext(key, scoped.scope, state), state, value) ) )
|
||||
|
||||
def triggeredBy(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Keys.triggeredBy)
|
||||
def runBefore(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Keys.runBefore)
|
||||
private[this] def nonLocal(tasks: Seq[AnyInitTask], key: AttributeKey[Seq[Task[_]]]): Initialize[Task[S]] =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
import sbt._
|
||||
import Keys._
|
||||
import complete.Parser
|
||||
import complete.DefaultParsers._
|
||||
import sbinary.DefaultProtocol._
|
||||
import Project.Initialize
|
||||
|
||||
object MyBuild extends Build
|
||||
{
|
||||
val keep = TaskKey[Int]("keep")
|
||||
val persisted = TaskKey[Int]("persist")
|
||||
val checkKeep = InputKey[Unit]("check-keep")
|
||||
val checkPersisted = InputKey[Unit]("check-persist")
|
||||
|
||||
val updateDemo = TaskKey[Int]("demo")
|
||||
val check = InputKey[Unit]("check")
|
||||
val sample = AttributeKey[Int]("demo-key")
|
||||
|
||||
def updateDemoInit = state map { s => (s get sample getOrElse 9) + 1 }
|
||||
|
||||
lazy val root = Project("root", file(".")) settings(
|
||||
updateDemo <<= updateDemoInit updateState demoState,
|
||||
check <<= checkInit,
|
||||
inMemorySetting,
|
||||
persistedSetting,
|
||||
inMemoryCheck,
|
||||
persistedCheck
|
||||
)
|
||||
def demoState(s: State, i: Int): State = s put (sample, i + 1)
|
||||
|
||||
def checkInit: Initialize[InputTask[Unit]] = InputTask( (_: State) => token(Space ~> IntBasic) ~ token(Space ~> IntBasic).?) { key =>
|
||||
(key, updateDemo, state) map { case ((curExpected, prevExpected), value, s) =>
|
||||
val prev = s get sample
|
||||
assert(value == curExpected, "Expected current value to be " + curExpected + ", got " + value)
|
||||
assert(prev == prevExpected, "Expected previous value to be " + prevExpected + ", got " + prev)
|
||||
}
|
||||
}
|
||||
|
||||
def inMemorySetting = keep <<= getPrevious(keep) map { case None => 3; case Some(x) => x + 1} keepAs(keep)
|
||||
def persistedSetting = persisted <<= loadPrevious(persisted) map { case None => 17; case Some(x) => x + 1} storeAs(keep)
|
||||
|
||||
def inMemoryCheck = checkKeep <<= inputCheck( (ctx, s) => Space ~> str(getFromContext(keep, ctx, s)) )
|
||||
def persistedCheck = checkPersisted <<= inputCheck( (ctx, s) => Space ~> str(loadFromContext(persisted, ctx, s)) )
|
||||
|
||||
def inputCheck[T](f: (ScopedKey[_], State) => Parser[T]): Initialize[InputTask[Unit]] =
|
||||
InputTask( resolvedScoped(ctx => (s: State) => f(ctx, s)) )( dummyTask )
|
||||
|
||||
def dummyTask = (key: Any) => maxErrors map { _ => () }
|
||||
def str(o: Option[Int]) = o match { case None => "blue"; case Some(i) => i.toString }
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
> check 10
|
||||
> check 12 11
|
||||
> check 14 13
|
||||
> reload
|
||||
> check 16 15
|
||||
|
||||
|
||||
-> check-keep 3
|
||||
> check-keep blue
|
||||
|
||||
> keep
|
||||
> check-keep 3
|
||||
-> check-keep 4
|
||||
> check-keep 3
|
||||
|
||||
> keep
|
||||
-> check-keep 3
|
||||
> check-keep 4
|
||||
> check-keep 4
|
||||
|
||||
> reload
|
||||
> check-keep blue
|
||||
> keep
|
||||
> check-keep 3
|
||||
|
||||
|
||||
-> check-persist 17
|
||||
> check-persist blue
|
||||
|
||||
> persist
|
||||
> check-persist 17
|
||||
-> check-persist blue
|
||||
|
||||
> persist
|
||||
> check-persist 18
|
||||
> reload
|
||||
> check-persist 18
|
||||
> persist
|
||||
> check-persist 19
|
||||
|
|
@ -22,13 +22,12 @@ object Task
|
|||
type Results[HL <: HList] = KList[Result, HL]
|
||||
}
|
||||
|
||||
final case class Task[T](info: Info, work: Action[T])
|
||||
final case class Task[T](info: Info[T], work: Action[T])
|
||||
{
|
||||
override def toString = info.name getOrElse ("Task(" + info + ")")
|
||||
override def hashCode = info.hashCode
|
||||
}
|
||||
/** `original` is used during transformation only.*/
|
||||
final case class Info(attributes: AttributeMap = AttributeMap.empty)
|
||||
final case class Info[T](attributes: AttributeMap = AttributeMap.empty, post: T => AttributeMap = const(AttributeMap.empty))
|
||||
{
|
||||
import Info._
|
||||
def name = attributes.get(Name)
|
||||
|
|
@ -36,12 +35,9 @@ final case class Info(attributes: AttributeMap = AttributeMap.empty)
|
|||
def setName(n: String) = set(Name, n)
|
||||
def setDescription(d: String) = set(Description, d)
|
||||
def set[T](key: AttributeKey[T], value: T) = copy(attributes = this.attributes.put(key, value))
|
||||
def postTransform[A](f: (T, AttributeMap) => AttributeMap) = copy(post = (t: T) => f(t, post(t)) )
|
||||
|
||||
override def toString =
|
||||
if(attributes.isEmpty)
|
||||
"_"
|
||||
else
|
||||
attributes.toString
|
||||
override def toString = if(attributes.isEmpty) "_" else attributes.toString
|
||||
}
|
||||
object Info
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ sealed trait TaskStreams[Key]
|
|||
final def readText(a: Key, sid: Option[String]): BufferedReader = readText(a, getID(sid))
|
||||
final def readBinary(a: Key, sid: Option[String]): BufferedInputStream = readBinary(a, getID(sid))
|
||||
|
||||
def key: Key
|
||||
def text(sid: String = default): PrintWriter
|
||||
def binary(sid: String = default): BufferedOutputStream
|
||||
|
||||
|
|
@ -91,7 +92,8 @@ object Streams
|
|||
opened ::= t
|
||||
t
|
||||
}
|
||||
|
||||
|
||||
def key: Key = a
|
||||
def open() {}
|
||||
|
||||
def close(): Unit = synchronized {
|
||||
|
|
|
|||
Loading…
Reference in New Issue