mirror of https://github.com/sbt/sbt.git
TaskKey[T].previous: Option[T], which returns the value of the task the last time it executed.
This requires a Format[T] to be implicitly available at the call site and requires the task to be referenced statically (not in a settingDyn call). References to previous task values in the form of a ScopedKey[Task[T]] + Format[T] are collected at setting load time in the 'references' setting. These are used to know which tasks should be persisted (the ScopedKey) and how to persist them (the Format). When checking/delegating previous references, rules are slightly different. A normal reference from a task t in scope s cannot refer to t in s unless there is an earlier definition of t in s. However, a previous reference does not have this restriction. This commit modifies validateReferenced to allow this. TODO: user documentation TODO: stable selection of the Format when there are multiple .previous calls on the same task TODO: make it usable in InputTasks, specifically Parsers
This commit is contained in:
parent
5d49fcbe78
commit
c669606999
|
|
@ -14,6 +14,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by")
|
||||
val runBefore = AttributeKey[Seq[Task[_]]]("run-before")
|
||||
val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped", "The ScopedKey for the referencing setting or task.", KeyRanks.DSetting)
|
||||
private[sbt] val taskDefinitionKey = AttributeKey[ScopedKey[_]]("task-definition-key", "Internal: used to map a task back to its ScopedKey.", Invisible)
|
||||
|
||||
lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None)
|
||||
def showFullKey(keyNameColor: Option[String]): Show[ScopedKey[_]] =
|
||||
|
|
@ -63,7 +64,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
import language.experimental.macros
|
||||
import std.TaskMacro.{inputTaskMacroImpl, inputTaskDynMacroImpl, taskDynMacroImpl, taskMacroImpl}
|
||||
import std.SettingMacro.{settingDynMacroImpl,settingMacroImpl}
|
||||
import std.{InputEvaluated, MacroValue, MacroTaskValue, ParserInput}
|
||||
import std.{InputEvaluated, MacroPrevious, MacroValue, MacroTaskValue, ParserInput}
|
||||
|
||||
def task[T](t: T): Def.Initialize[Task[T]] = macro taskMacroImpl[T]
|
||||
def taskDyn[T](t: Def.Initialize[Task[T]]): Def.Initialize[Task[T]] = macro taskDynMacroImpl[T]
|
||||
|
|
@ -79,6 +80,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
implicit def macroValueIT[T](in: Initialize[Task[T]]): MacroValue[T] = ???
|
||||
implicit def macroValueIInT[T](in: Initialize[InputTask[T]]): InputEvaluated[T] = ???
|
||||
implicit def taskMacroValueIT[T](in: Initialize[Task[T]]): MacroTaskValue[T] = ???
|
||||
implicit def macroPrevious[T](in: TaskKey[T]): MacroPrevious[T] = ???
|
||||
|
||||
// The following conversions enable the types Parser[T], Initialize[Parser[T]], and Initialize[State => Parser[T]] to
|
||||
// be used in the inputTask macro as an input with an ultimate result of type T
|
||||
|
|
@ -100,6 +102,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
private[sbt] def isDummy(t: Task[_]): Boolean = t.info.attributes.get(isDummyTask) getOrElse false
|
||||
private[sbt] val isDummyTask = AttributeKey[Boolean]("is-dummy-task", "Internal: used to identify dummy tasks. sbt injects values for these tasks at the start of task execution.", Invisible)
|
||||
private[sbt] val (stateKey, dummyState) = dummy[State]("state", "Current build state.")
|
||||
private[sbt] val (streamsManagerKey, dummyStreamsManager) = Def.dummy[std.Streams[ScopedKey[_]]]("streams-manager", "Streams manager, which provides streams for different contexts.")
|
||||
}
|
||||
// these need to be mixed into the sbt package object because the target doesn't involve Initialize or anything in Def
|
||||
trait TaskMacroExtra
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
package sbt
|
||||
|
||||
import Def.{Initialize, resolvedScoped, ScopedKey, Setting, streamsManagerKey}
|
||||
import Previous._
|
||||
import Types._
|
||||
|
||||
import java.io.{InputStream, OutputStream}
|
||||
import sbinary.{DefaultProtocol,Format}
|
||||
import DefaultProtocol.{StringFormat, withStamp}
|
||||
|
||||
/** Reads the previous value of tasks on-demand. The read values are cached so that they are only read once per task execution.
|
||||
* `referenced` provides the `Format` to use for each key. */
|
||||
private[sbt] final class Previous(streams: Streams, referenced: IMap[ScopedTaskKey, Referenced])
|
||||
{
|
||||
private[this] val map = referenced.mapValues(toValue)
|
||||
private[this] def toValue = new (Referenced ~> ReferencedValue) { def apply[T](x: Referenced[T]) = new ReferencedValue(x) }
|
||||
|
||||
private[this] final class ReferencedValue[T](referenced: Referenced[T])
|
||||
{
|
||||
import referenced.{stamped, task}
|
||||
lazy val previousValue: Option[T] = {
|
||||
val in = streams(task).readBinary(task, StreamName)
|
||||
try read(in, stamped) finally in.close()
|
||||
}
|
||||
}
|
||||
|
||||
/** Used by the .previous runtime implemention to get the previous value for task `key`. */
|
||||
private def get[T](key: ScopedKey[Task[T]]): Option[T] =
|
||||
map.get(key).flatMap(_.previousValue)
|
||||
}
|
||||
object Previous
|
||||
{
|
||||
private[sbt] type ScopedTaskKey[T] = ScopedKey[Task[T]]
|
||||
private type Streams = sbt.std.Streams[ScopedKey[_]]
|
||||
|
||||
/** The stream where the task value is persisted. */
|
||||
private final val StreamName = "previous"
|
||||
|
||||
/** Represents a reference task.previous*/
|
||||
private[sbt] final class Referenced[T](val task: ScopedKey[Task[T]], val format: Format[T]) {
|
||||
lazy val stamped = withStamp(task.key.manifest.toString)(format)
|
||||
def setTask(newTask: ScopedKey[Task[T]]) = new Referenced(newTask, format)
|
||||
}
|
||||
|
||||
private[sbt] val references = SettingKey[References]("previous-references", "Collects all static references to previous values of tasks.", KeyRanks.Invisible)
|
||||
private[sbt] val cache = TaskKey[Previous]("previous-cache", "Caches previous values of tasks read from disk for the duration of a task execution.", KeyRanks.Invisible)
|
||||
private[this] val previousReferenced = AttributeKey[Referenced[_]]("previous-referenced")
|
||||
|
||||
/** Records references to previous task value. This should be completely populated after settings finish loading. */
|
||||
private[sbt] final class References
|
||||
{
|
||||
private[this] var map = IMap.empty[ScopedTaskKey, Referenced]
|
||||
|
||||
// TODO: this arbitrarily chooses a Format.
|
||||
// The need to choose is a fundamental problem with this approach, but this should at least make a stable choice.
|
||||
def recordReference[T](key: ScopedKey[Task[T]], format: Format[T]): Unit = synchronized {
|
||||
map = map.put(key, new Referenced(key, format))
|
||||
}
|
||||
def getReferences: IMap[ScopedTaskKey, Referenced] = synchronized { map }
|
||||
}
|
||||
|
||||
/** Persists values of tasks t where there is some task referencing it via t.previous. */
|
||||
private[sbt] def complete(referenced: References, results: RMap[Task,Result], streams: Streams): Unit =
|
||||
{
|
||||
val map = referenced.getReferences
|
||||
def impl[T](key: ScopedKey[_], result: T): Unit =
|
||||
for(i <- map.get(key.asInstanceOf[ScopedTaskKey[T]])) {
|
||||
val out = streams.apply(i.task).binary(StreamName)
|
||||
try write(out, i.stamped, result ) finally out.close()
|
||||
}
|
||||
|
||||
for {
|
||||
results.TPair(Task(info, _), Value(result)) <- results.toTypedSeq
|
||||
key <- info.attributes get Def.taskDefinitionKey
|
||||
}
|
||||
impl(key, result)
|
||||
}
|
||||
|
||||
private def read[T](stream: InputStream, format: Format[T]): Option[T] =
|
||||
try Some(format.reads(stream))
|
||||
catch { case e: Exception => None }
|
||||
|
||||
private def write[T](stream: OutputStream, format: Format[T], value: T): Unit =
|
||||
try format.writes(stream, value)
|
||||
catch { case e: Exception => () }
|
||||
|
||||
/** Public as a macro implementation detail. Do not call directly. */
|
||||
def runtime[T](skey: TaskKey[T])(implicit format: Format[T]): Initialize[Task[Option[T]]] =
|
||||
{
|
||||
val inputs = (cache in Global) zip Def.validated(skey, selfRefOk=true) zip (references in Global)
|
||||
inputs { case ( (prevTask, resolved), refs ) =>
|
||||
refs.recordReference(resolved, format) // always evaluated on project load
|
||||
import std.TaskExtra._
|
||||
prevTask.map(_ get resolved) // evaluated if this task is evaluated
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] def cacheSetting = (streamsManagerKey, references) map { (s, refs) => new Previous(s, refs.getReferences) }
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ object FullConvert extends Convert
|
|||
{
|
||||
import InputWrapper._
|
||||
def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] =
|
||||
if(nme == WrapInitTaskName)
|
||||
if(nme == WrapInitTaskName || nme == WrapPreviousName)
|
||||
Converted.Success(in)
|
||||
else if(nme == WrapInitName)
|
||||
{
|
||||
|
|
@ -83,4 +83,3 @@ object InitParserConvert extends Convert
|
|||
else
|
||||
Converted.NotApplicable
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package std
|
|||
import reflect.macros._
|
||||
import reflect.internal.annotations.compileTimeOnly
|
||||
|
||||
import Def.Initialize
|
||||
import Def.{Initialize, ScopedKey}
|
||||
import appmacro.ContextUtil
|
||||
import complete.Parser
|
||||
|
||||
|
|
@ -22,6 +22,7 @@ object InputWrapper
|
|||
private[std] final val WrapInitTaskName = "wrapInitTask_\u2603\u2603"
|
||||
private[std] final val WrapInitInputName = "wrapInitInputTask_\u2603\u2603"
|
||||
private[std] final val WrapInputName = "wrapInputTask_\u2603\u2603"
|
||||
private[std] final val WrapPreviousName = "wrapPrevious_\u2603\u2603"
|
||||
|
||||
@compileTimeOnly("`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.")
|
||||
def wrapTask_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
|
@ -38,6 +39,9 @@ object InputWrapper
|
|||
@compileTimeOnly("`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.")
|
||||
def wrapInitInputTask_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
@compileTimeOnly("`previous` can only be called on a task within a task or input task definition macro, such as :=, +=, ++=, Def.task, or Def.inputTask.")
|
||||
def wrapPrevious_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
private[this] def implDetailError = error("This method is an implementation detail and should not be referenced.")
|
||||
|
||||
private[std] def wrapTask[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
|
|
@ -52,6 +56,9 @@ object InputWrapper
|
|||
private[std] def wrapInputTask[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapInputName)(ts, pos)
|
||||
|
||||
private[std] def wrapPrevious[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[Option[T]] =
|
||||
wrapImpl[Option[T],InputWrapper.type](c, InputWrapper, WrapPreviousName)(ts, pos)
|
||||
|
||||
/** Wraps an arbitrary Tree in a call to the `<s>.<wrapName>` method of this module for later processing by an enclosing macro.
|
||||
* The resulting Tree is the manually constructed version of:
|
||||
*
|
||||
|
|
@ -96,6 +103,22 @@ object InputWrapper
|
|||
else
|
||||
c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.widen}")
|
||||
}
|
||||
/** Translates <task: TaskKey[T]>.previous(format) to Previous.runtime(<task>)(format).value*/
|
||||
def previousMacroImpl[T: c.WeakTypeTag](c: Context)(format: c.Expr[sbinary.Format[T]]): c.Expr[Option[T]] =
|
||||
{
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case a @ Apply(Select(Apply(_, t :: Nil), tp), fmt) =>
|
||||
if(t.tpe <:< c.weakTypeOf[TaskKey[T]]) {
|
||||
val tsTyped = c.Expr[TaskKey[T]](t)
|
||||
val newTree = c.universe.reify { Previous.runtime[T](tsTyped.splice)(format.splice) }
|
||||
wrapPrevious[T](c)(newTree, a.pos)
|
||||
}
|
||||
else
|
||||
c.abort(a.pos, s"Internal sbt error. Unexpected type ${t.tpe.widen}")
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed abstract class MacroTaskValue[T] {
|
||||
|
|
@ -118,6 +141,10 @@ sealed abstract class ParserInputTask[T] {
|
|||
@compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def parsed: Task[T] = macro ParserInput.parsedInputMacroImpl[T]
|
||||
}
|
||||
sealed abstract class MacroPrevious[T] {
|
||||
@compileTimeOnly("`previous` can only be used within a task macro, such as :=, +=, ++=, or Def.task.")
|
||||
def previous(implicit format: sbinary.Format[T]): Option[T] = macro InputWrapper.previousMacroImpl[T]
|
||||
}
|
||||
|
||||
/** Implementation detail. The wrap method temporarily holds the input parser (as a Tree, at compile time) until the input task macro processes it. */
|
||||
object ParserInput {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ object InitializeConvert extends Convert
|
|||
}
|
||||
else if(nme == InputWrapper.WrapTaskName || nme == InputWrapper.WrapInitTaskName)
|
||||
Converted.Failure(in.pos, "A setting cannot depend on a task")
|
||||
else if(nme == InputWrapper.WrapPreviousName)
|
||||
Converted.Failure(in.pos, "A setting cannot depend on a task's previous value.")
|
||||
else
|
||||
Converted.NotApplicable
|
||||
}
|
||||
|
|
@ -38,6 +40,6 @@ object SettingMacro
|
|||
def settingMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[T]] =
|
||||
Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(Left(t), Instance.idTransform[c.type])
|
||||
|
||||
def settingDynMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Initialize[T]]): c.Expr[Initialize[T]] =
|
||||
def settingDynMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Initialize[T]]): c.Expr[Initialize[T]] =
|
||||
Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(Right(t), Instance.idTransform[c.type])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ object Defaults extends BuildCommon
|
|||
buildDependencies <<= Classpaths.constructBuildDependencies,
|
||||
taskTemporaryDirectory := { val dir = IO.createTemporaryDirectory; dir.deleteOnExit(); dir },
|
||||
onComplete := { val dir = taskTemporaryDirectory.value; () => {IO.delete(dir); IO.createDirectory(dir) }},
|
||||
Previous.cache <<= Previous.cacheSetting,
|
||||
Previous.references :== new Previous.References,
|
||||
concurrentRestrictions <<= defaultRestrictions,
|
||||
parallelExecution :== true,
|
||||
sbtVersion := appConfiguration.value.provider.id.version,
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@ object EvaluateTask
|
|||
import std.{TaskExtra,Transform}
|
||||
import TaskExtra._
|
||||
import Keys.state
|
||||
|
||||
private[sbt] def defaultProgress: ExecuteProgress[Task] =
|
||||
|
||||
private[sbt] def defaultProgress: ExecuteProgress[Task] =
|
||||
if(java.lang.Boolean.getBoolean("sbt.task.timings")) new TaskTimings else ExecuteProgress.empty[Task]
|
||||
|
||||
val SystemProcessors = Runtime.getRuntime.availableProcessors
|
||||
|
||||
@deprecated("Use extractedConfig.", "0.13.0")
|
||||
def defaultConfig(state: State): EvaluateConfig =
|
||||
def defaultConfig(state: State): EvaluateConfig =
|
||||
{
|
||||
val extracted = Project.extract(state)
|
||||
defaultConfig(extracted, extracted.structure)
|
||||
|
|
@ -155,7 +155,7 @@ object EvaluateTask
|
|||
val str = std.Streams.closeable(structure.streams(state))
|
||||
try { f(str) } finally { str.close() }
|
||||
}
|
||||
|
||||
|
||||
def getTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, streams: Streams, ref: ProjectRef): Option[(Task[T], NodeView[Task])] =
|
||||
{
|
||||
val thisScope = Load.projectScope(ref)
|
||||
|
|
@ -169,7 +169,7 @@ object EvaluateTask
|
|||
def runTask[T](root: Task[T], state: State, streams: Streams, triggers: Triggers[Task], config: EvaluateConfig)(implicit taskToNode: NodeView[Task]): (State, Result[T]) =
|
||||
{
|
||||
import ConcurrentRestrictions.{completionService, TagMap, Tag, tagged, tagsKey}
|
||||
|
||||
|
||||
val log = state.log
|
||||
log.debug("Running task... Cancelable: " + config.cancelable + ", check cycles: " + config.checkCycles)
|
||||
val tags = tagged[Task[_]](_.info get tagsKey getOrElse Map.empty, Tags.predicate(config.restrictions))
|
||||
|
|
@ -183,8 +183,11 @@ object EvaluateTask
|
|||
def run() = {
|
||||
val x = new Execute[Task]( Execute.config(config.checkCycles, overwriteNode), triggers, config.progress)(taskToNode)
|
||||
val (newState, result) =
|
||||
try applyResults(x.runKeep(root)(service), state, root)
|
||||
catch { case inc: Incomplete => (state, Inc(inc)) }
|
||||
try {
|
||||
val results = x.runKeep(root)(service)
|
||||
storeValuesForPrevious(results, state, streams)
|
||||
applyResults(results, state, root)
|
||||
} catch { case inc: Incomplete => (state, Inc(inc)) }
|
||||
finally shutdown()
|
||||
val replaced = transformInc(result)
|
||||
logIncResult(replaced, state, streams)
|
||||
|
|
@ -201,6 +204,10 @@ object EvaluateTask
|
|||
run()
|
||||
}
|
||||
|
||||
private[this] def storeValuesForPrevious(results: RMap[Task, Result], state: State, streams: Streams): Unit =
|
||||
for(referenced <- Previous.references in Global get Project.structure(state).data)
|
||||
Previous.complete(referenced, results, streams)
|
||||
|
||||
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 =
|
||||
|
|
@ -210,7 +217,7 @@ object EvaluateTask
|
|||
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 ) }
|
||||
|
|
|
|||
|
|
@ -55,8 +55,6 @@ object Keys
|
|||
val transformState = AttributeKey[State => State]("transform-state", "State transformation to apply after tasks run.", DSetting)
|
||||
|
||||
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.", DSetting)
|
||||
// https://issues.scala-lang.org/browse/SI-2915
|
||||
// val onComplete = SettingKey[RMap[Task,Result] => RMap[Task,Result]]("on-complete", "Transformation to apply to the final task result map. This may also be used to register hooks to run when task evaluation completes.", DSetting)
|
||||
|
||||
// Command keys
|
||||
val historyPath = SettingKey(BasicKeys.historyPath)
|
||||
|
|
@ -327,17 +325,19 @@ object Keys
|
|||
val cancelable = SettingKey[Boolean]("cancelable", "Enables (true) or disables (false) the ability to interrupt task execution with CTRL+C.", BMinusSetting)
|
||||
val settingsData = std.FullInstance.settingsData
|
||||
val streams = TaskKey[TaskStreams]("streams", "Provides streams for logging and persisting data.", DTask)
|
||||
val taskDefinitionKey = AttributeKey[ScopedKey[_]]("task-definition-key", "Internal: used to map a task back to its ScopedKey.", Invisible)
|
||||
val taskDefinitionKey = Def.taskDefinitionKey
|
||||
val (executionRoots, dummyRoots)= Def.dummy[Seq[ScopedKey[_]]]("execution-roots", "The list of root tasks for this task execution. Roots are the top-level tasks that were directly requested to be run.")
|
||||
|
||||
val state = Def.stateKey
|
||||
val streamsManager = Def.streamsManagerKey
|
||||
|
||||
@deprecated("Implementation detail.", "0.13.1")
|
||||
val isDummyTask = Def.isDummyTask
|
||||
@deprecated("Implementation detail.", "0.13.1")
|
||||
val dummyState = Def.dummyState
|
||||
@deprecated("Implementation detail.", "0.13.2")
|
||||
val dummyStreamsManager = Def.dummyStreamsManager
|
||||
|
||||
val (streamsManager, dummyStreamsManager) = Def.dummy[Streams]("streams-manager", "Streams manager, which provides streams for different contexts.")
|
||||
val stateStreams = AttributeKey[Streams]("streams-manager", "Streams manager, which provides streams for different contexts. Setting this on State will override the default Streams implementation.")
|
||||
val resolvedScoped = Def.resolvedScoped
|
||||
val pluginData = TaskKey[PluginData]("plugin-data", "Information from the plugin build needed in the main build definition.", DTask)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ object Load
|
|||
config.copy(injectSettings = config.injectSettings.copy(projectLoaded = compiled))
|
||||
}
|
||||
def buildGlobalSettings(base: File, files: Seq[File], config: sbt.LoadBuildConfiguration): ClassLoader => Seq[Setting[_]] =
|
||||
{
|
||||
{
|
||||
val eval = mkEval(data(config.classpath), base, defaultEvalOptions)
|
||||
val imports = BuildUtil.baseImports ++ BuildUtil.importAllRoot(config.globalPluginNames)
|
||||
loader => EvaluateConfigurations(eval, files, imports)(loader).settings
|
||||
|
|
@ -200,7 +200,7 @@ object Load
|
|||
def buildConfigurations(loaded: sbt.LoadedBuild, rootProject: URI => String, injectSettings: InjectSettings): Seq[Setting[_]] =
|
||||
{
|
||||
((loadedBuild in GlobalScope :== loaded) +:
|
||||
transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++
|
||||
transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++
|
||||
inScope(GlobalScope)( pluginGlobalSettings(loaded) ) ++
|
||||
loaded.units.toSeq.flatMap { case (uri, build) =>
|
||||
val plugins = build.unit.plugins.plugins
|
||||
|
|
@ -234,7 +234,7 @@ object Load
|
|||
def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)
|
||||
def projectScope(project: Reference): Scope = Scope(Select(project), Global, Global, Global)
|
||||
|
||||
|
||||
def lazyEval(unit: sbt.BuildUnit): () => Eval =
|
||||
{
|
||||
lazy val eval = mkEval(unit)
|
||||
|
|
@ -438,7 +438,7 @@ object Load
|
|||
new sbt.BuildUnit(uri, normBase, loadedDefs, plugs)
|
||||
}
|
||||
|
||||
private[this] def autoID(localBase: File, context: PluginManagement.Context, existingIDs: Seq[String]): String =
|
||||
private[this] def autoID(localBase: File, context: PluginManagement.Context, existingIDs: Seq[String]): String =
|
||||
{
|
||||
def normalizeID(f: File) = Project.normalizeProjectID(f.getName) match {
|
||||
case Right(id) => id
|
||||
|
|
@ -453,11 +453,11 @@ object Load
|
|||
if(existingIDs.contains(tryID)) Build.defaultID(localBase) else tryID
|
||||
}
|
||||
|
||||
private[this] def autoIDError(base: File, reason: String): String =
|
||||
private[this] def autoIDError(base: File, reason: String): String =
|
||||
"Could not derive root project ID from directory " + base.getAbsolutePath + ":\n" +
|
||||
reason + "\nRename the directory or explicitly define a root project."
|
||||
|
||||
private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] =
|
||||
private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] =
|
||||
b.projectDefinitions(base).map(resolveBase(base))
|
||||
|
||||
private[this] def loadTransitive(newProjects: Seq[Project], buildBase: File, imports: Seq[String], plugins: sbt.LoadedPlugins, eval: () => Eval, injectSettings: InjectSettings, acc: Seq[Project], memoSettings: mutable.Map[File, LoadedSbtFile]): Seq[Project] =
|
||||
|
|
@ -483,7 +483,7 @@ object Load
|
|||
else
|
||||
loadTransitive(nextProjects, buildBase, imports, plugins, eval, injectSettings, loadedProjects, memoSettings)
|
||||
}
|
||||
|
||||
|
||||
private[this] def loadSettings(auto: AddSettings, projectBase: File, buildImports: Seq[String], loadedPlugins: sbt.LoadedPlugins, eval: ()=>Eval, injectSettings: InjectSettings, memoSettings: mutable.Map[File, LoadedSbtFile]): LoadedSbtFile =
|
||||
{
|
||||
lazy val defaultSbtFiles = configurationSources(projectBase)
|
||||
|
|
@ -570,7 +570,7 @@ object Load
|
|||
def pluginDefinitionLoader(config: sbt.LoadBuildConfiguration, dependencyClasspath: Seq[Attributed[File]]): (Seq[Attributed[File]], ClassLoader) =
|
||||
pluginDefinitionLoader(config, dependencyClasspath, Nil)
|
||||
def pluginDefinitionLoader(config: sbt.LoadBuildConfiguration, pluginData: PluginData): (Seq[Attributed[File]], ClassLoader) =
|
||||
pluginDefinitionLoader(config, pluginData.dependencyClasspath, pluginData.definitionClasspath)
|
||||
pluginDefinitionLoader(config, pluginData.dependencyClasspath, pluginData.definitionClasspath)
|
||||
def pluginDefinitionLoader(config: sbt.LoadBuildConfiguration, depcp: Seq[Attributed[File]], defcp: Seq[Attributed[File]]): (Seq[Attributed[File]], ClassLoader) =
|
||||
{
|
||||
val definitionClasspath =
|
||||
|
|
@ -670,7 +670,7 @@ object Load
|
|||
|
||||
def initialSession(structure: sbt.BuildStructure, rootEval: () => Eval): SessionSettings =
|
||||
new SessionSettings(structure.root, projectMap(structure, Map.empty), structure.settings, Map.empty, Nil, rootEval)
|
||||
|
||||
|
||||
def projectMap(structure: sbt.BuildStructure, current: Map[URI, String]): Map[URI, String] =
|
||||
{
|
||||
val units = structure.units
|
||||
|
|
@ -699,7 +699,7 @@ object Load
|
|||
def getImports(unit: sbt.BuildUnit): Seq[String] = BuildUtil.getImports(unit)
|
||||
|
||||
def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced }
|
||||
|
||||
|
||||
@deprecated("LoadedBuildUnit is now top-level", "0.13.0")
|
||||
type LoadedBuildUnit = sbt.LoadedBuildUnit
|
||||
|
||||
|
|
@ -711,7 +711,7 @@ object Load
|
|||
|
||||
@deprecated("LoadBuildConfiguration is now top-level", "0.13.0")
|
||||
type LoadBuildConfiguration = sbt.LoadBuildConfiguration
|
||||
@deprecated("LoadBuildConfiguration is now top-level", "0.13.0")
|
||||
@deprecated("LoadBuildConfiguration is now top-level", "0.13.0")
|
||||
val LoadBuildConfiguration = sbt.LoadBuildConfiguration
|
||||
|
||||
final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]])
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import sbinary.DefaultProtocol._
|
||||
|
||||
lazy val a0 = 1
|
||||
lazy val b0 = 1
|
||||
lazy val a = taskKey[Int]("a")
|
||||
lazy val b = taskKey[Int]("b")
|
||||
lazy val next = taskKey[(Int,Int)]("next")
|
||||
lazy val checkNext = inputKey[Unit]("check-next")
|
||||
|
||||
|
||||
// These test that there is no cycle when referring to previous values (a -> b.previous, b -> a.previous)
|
||||
// Also, it is ok for b to refer to b.previous:
|
||||
// normally, b's definition could not refer to plain b.value because it would be undefined
|
||||
|
||||
a := b.previous.getOrElse(a0)
|
||||
|
||||
b := a.previous.getOrElse(a0) + b.previous.getOrElse(b0)
|
||||
|
||||
next := (a.value, b.value)
|
||||
|
||||
def parser = {
|
||||
import complete.DefaultParsers._
|
||||
(Space ~> IntBasic) ~ (Space ~> IntBasic)
|
||||
}
|
||||
|
||||
checkNext := {
|
||||
val (expectedA, expectedB) = parser.parsed
|
||||
val actualA = a.value
|
||||
val actualB = b.value
|
||||
assert(actualA == expectedA, s"Expected 'a' to be $expectedA, got $actualA")
|
||||
assert(actualB == expectedB, s"Expected 'b' to be $expectedB, got $actualB")
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import sbinary.DefaultProtocol._
|
||||
|
||||
lazy val x = taskKey[Int]("x")
|
||||
lazy val y = taskKey[Int]("y")
|
||||
lazy val checkScopes = inputKey[Unit]("check scopes")
|
||||
|
||||
lazy val subA = project
|
||||
lazy val subB = project
|
||||
|
||||
x := 3
|
||||
|
||||
x in Compile in y := 7
|
||||
|
||||
x in Runtime in y := 13
|
||||
|
||||
x in subA in Compile := {
|
||||
val xcy = (x in Compile in y).previous getOrElse 0 // 7
|
||||
// verify that This is properly resolved to Global and not the defining key's scope
|
||||
val xg = x.previous getOrElse 0 // 3
|
||||
println(s"xcy=$xcy, xg=$xg")
|
||||
xcy * xg
|
||||
}
|
||||
|
||||
|
||||
inConfig(Compile)(Seq(
|
||||
y in subB := {
|
||||
// verify that the referenced key gets delegated
|
||||
val xty = (x in Test in y).previous getOrElse 0 // 13
|
||||
// verify that inConfig gets applied
|
||||
val xcy = (x in y).previous getOrElse 0 // 7
|
||||
println(s"xcy=$xcy, xty=$xty")
|
||||
xty * xcy
|
||||
}
|
||||
))
|
||||
|
||||
def parser = {
|
||||
import complete.DefaultParsers._
|
||||
(Space ~> IntBasic) ~ (Space ~> IntBasic)
|
||||
}
|
||||
|
||||
checkScopes := {
|
||||
val (expectedX, expectedY) = parser.parsed
|
||||
val actualX = (x in subA in Compile).value
|
||||
val actualY = (y in subB in Test).value
|
||||
assert(actualX == expectedX, s"Expected 'x' to be $expectedX, got $actualX")
|
||||
assert(actualY == expectedY, s"Expected 'y' to be $expectedY, got $actualY")
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
> debug
|
||||
|
||||
> checkNext 1 2
|
||||
> checkNext 2 3
|
||||
> checkNext 3 5
|
||||
> checkNext 5 8
|
||||
> clean
|
||||
> checkNext 1 2
|
||||
|
||||
> checkScopes 0 0
|
||||
> all x compile:y::x runtime:y::x y
|
||||
> checkScopes 21 91
|
||||
|
|
@ -27,9 +27,10 @@ abstract class EvaluateSettings[Scope]
|
|||
case k: Keyed[s, T] => single(getStatic(k.scopedKey), k.transform)
|
||||
case a: Apply[k,T] => new MixedNode[k,T]( a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist)
|
||||
case b: Bind[s,T] => new BindNode[s,T]( transform(b.in), x => transform(b.f(x)))
|
||||
case init.StaticScopes => constant(() => allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope]
|
||||
case init.StaticScopes => strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope]
|
||||
case v: Value[T] => constant(v.value)
|
||||
case t: TransformCapture => constant(() => t.f)
|
||||
case v: ValidationCapture[T] => strictConstant(v.key)
|
||||
case t: TransformCapture => strictConstant(t.f)
|
||||
case o: Optional[s,T] => o.a match {
|
||||
case None => constant( () => o.f(None) )
|
||||
case Some(i) => single[s,T](transform(i), x => o.f(Some(x)))
|
||||
|
|
@ -80,7 +81,7 @@ abstract class EvaluateSettings[Scope]
|
|||
private[this] def workComplete(): Unit =
|
||||
if(running.decrementAndGet() == 0)
|
||||
complete.put( None )
|
||||
|
||||
|
||||
private[this] sealed abstract class INode[T]
|
||||
{
|
||||
private[this] var state: EvaluationState = New
|
||||
|
|
@ -92,9 +93,9 @@ abstract class EvaluateSettings[Scope]
|
|||
override def toString = getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " +
|
||||
keyString
|
||||
|
||||
private[this] def keyString =
|
||||
private[this] def keyString =
|
||||
(static.toSeq.flatMap { case (key, value) => if(value eq this) init.showFullKey(key) :: Nil else Nil }).headOption getOrElse "non-static"
|
||||
|
||||
|
||||
final def get: T = synchronized {
|
||||
assert(value != null, toString + " not evaluated")
|
||||
value
|
||||
|
|
@ -103,7 +104,7 @@ abstract class EvaluateSettings[Scope]
|
|||
val ready = state == Evaluated
|
||||
if(!ready) blocking += from
|
||||
registerIfNew()
|
||||
ready
|
||||
ready
|
||||
}
|
||||
final def isDone: Boolean = synchronized { state == Evaluated }
|
||||
final def isNew: Boolean = synchronized { state == New }
|
||||
|
|
@ -119,7 +120,7 @@ abstract class EvaluateSettings[Scope]
|
|||
else
|
||||
state = Blocked
|
||||
}
|
||||
|
||||
|
||||
final def schedule(): Unit = synchronized {
|
||||
assert(state == New || state == Blocked, "Invalid state for schedule() call: " + toString)
|
||||
state = Ready
|
||||
|
|
@ -158,6 +159,7 @@ abstract class EvaluateSettings[Scope]
|
|||
protected def evaluate0(): Unit
|
||||
}
|
||||
|
||||
private[this] def strictConstant[T](v: T): INode[T] = constant(() => v)
|
||||
private[this] def constant[T](f: () => T): INode[T] = new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty)
|
||||
private[this] def single[S,T](in: INode[S], f: S => T): INode[T] = new MixedNode[ ({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S])
|
||||
private[this] final class BindNode[S,T](in: INode[S], f: S => INode[T]) extends INode[T]
|
||||
|
|
|
|||
|
|
@ -59,9 +59,14 @@ trait Init[Scope]
|
|||
type ScopeLocal = ScopedKey[_] => Seq[Setting[_]]
|
||||
type MapConstant = ScopedKey ~> Option
|
||||
|
||||
private[sbt] abstract class ValidateKeyRef {
|
||||
def apply[T](key: ScopedKey[T], selfRefOk: Boolean): ValidatedRef[T]
|
||||
}
|
||||
|
||||
/** The result of this initialization is the composition of applied transformations.
|
||||
* This can be useful when dealing with dynamic Initialize values. */
|
||||
lazy val capturedTransformations: Initialize[Initialize ~> Initialize] = new TransformCapture(idK[Initialize])
|
||||
|
||||
def setting[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition = NoPosition): Setting[T] = new Setting[T](key, init, pos)
|
||||
def valueStrict[T](value: T): Initialize[T] = pure(() => value)
|
||||
def value[T](value: => T): Initialize[T] = pure(value _)
|
||||
|
|
@ -74,10 +79,15 @@ trait Init[Scope]
|
|||
def uniform[S,T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] =
|
||||
new Apply[({ type l[L[x]] = List[L[S]] })#l, T](f, inputs.toList, AList.seq[S])
|
||||
|
||||
/** The result of this initialization is the validated `key`.
|
||||
* No dependency is introduced on `key`. If `selfRefOk` is true, validation will not fail if the key is referenced by a definition of `key`.
|
||||
* That is, key := f(validated(key).value) is allowed only if `selfRefOk == true`. */
|
||||
private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk)
|
||||
|
||||
/** Constructs a derived setting that will be automatically defined in every scope where one of its dependencies
|
||||
* is explicitly defined and the where the scope matches `filter`.
|
||||
* A setting initialized with dynamic dependencies is only allowed if `allowDynamic` is true.
|
||||
* Only the static dependencies are tracked, however. */
|
||||
* Only the static dependencies are tracked, however. Dependencies on previous values do not introduce a derived setting either. */
|
||||
final def derive[T](s: Setting[T], allowDynamic: Boolean = false, filter: Scope => Boolean = const(true), trigger: AttributeKey[_] => Boolean = const(true)): Setting[T] = {
|
||||
deriveAllowed(s, allowDynamic) foreach error
|
||||
new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger, nextDefaultID())
|
||||
|
|
@ -158,12 +168,12 @@ trait Init[Scope]
|
|||
|
||||
def delegate(sMap: ScopedMap)(implicit delegates: Scope => Seq[Scope], display: Show[ScopedKey[_]]): ScopedMap =
|
||||
{
|
||||
def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateRef { def apply[T](k: ScopedKey[T]) =
|
||||
delegateForKey(sMap, k, delegates(k.scope), ref, isFirst)
|
||||
def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateKeyRef { def apply[T](k: ScopedKey[T], selfRefOk: Boolean) =
|
||||
delegateForKey(sMap, k, delegates(k.scope), ref, selfRefOk || !isFirst)
|
||||
}
|
||||
type ValidatedSettings[T] = Either[Seq[Undefined], SettingSeq[T]]
|
||||
val f = new (SettingSeq ~> ValidatedSettings) { def apply[T](ks: Seq[Setting[T]]) = {
|
||||
val (undefs, valid) = Util.separate(ks.zipWithIndex){ case (s,i) => s validateReferenced refMap(s, i == 0) }
|
||||
val (undefs, valid) = Util.separate(ks.zipWithIndex){ case (s,i) => s validateKeyReferenced refMap(s, i == 0) }
|
||||
if(undefs.isEmpty) Right(valid) else Left(undefs.flatten)
|
||||
}}
|
||||
type Undefs[_] = Seq[Undefined]
|
||||
|
|
@ -173,10 +183,10 @@ trait Init[Scope]
|
|||
else
|
||||
throw Uninitialized(sMap.keys.toSeq, delegates, undefineds.values.flatten.toList, false)
|
||||
}
|
||||
private[this] def delegateForKey[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], ref: Setting[_], isFirst: Boolean): Either[Undefined, ScopedKey[T]] =
|
||||
private[this] def delegateForKey[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], ref: Setting[_], selfRefOk: Boolean): Either[Undefined, ScopedKey[T]] =
|
||||
{
|
||||
val skeys = scopes.iterator.map(x => ScopedKey(x, k.key))
|
||||
val definedAt = skeys.find( sk => (!isFirst || ref.key != sk) && (sMap contains sk))
|
||||
val definedAt = skeys.find( sk => (selfRefOk || ref.key != sk) && (sMap contains sk))
|
||||
definedAt.toRight(Undefined(ref, k))
|
||||
}
|
||||
|
||||
|
|
@ -377,14 +387,25 @@ trait Init[Scope]
|
|||
{
|
||||
def dependencies: Seq[ScopedKey[_]]
|
||||
def apply[S](g: T => S): Initialize[S]
|
||||
|
||||
@deprecated("Will be made private.", "0.13.2")
|
||||
def mapReferenced(g: MapScoped): Initialize[T]
|
||||
def validateReferenced(g: ValidateRef): ValidatedInit[T]
|
||||
@deprecated("Will be made private.", "0.13.2")
|
||||
def mapConstant(g: MapConstant): Initialize[T]
|
||||
|
||||
@deprecated("Will be made private.", "0.13.2")
|
||||
def validateReferenced(g: ValidateRef): ValidatedInit[T] =
|
||||
validateKeyReferenced( new ValidateKeyRef { def apply[T](key: ScopedKey[T], selfRefOk: Boolean) = g(key) })
|
||||
|
||||
private[sbt] def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T]
|
||||
|
||||
def evaluate(map: Settings[Scope]): T
|
||||
def zip[S](o: Initialize[S]): Initialize[(T,S)] = zipTupled(o)(idFun)
|
||||
def zipWith[S,U](o: Initialize[S])(f: (T,S) => U): Initialize[U] = zipTupled(o)(f.tupled)
|
||||
private[this] def zipTupled[S,U](o: Initialize[S])(f: ((T,S)) => U): Initialize[U] =
|
||||
new Apply[({ type l[L[x]] = (L[T], L[S]) })#l, U](f, (this, o), AList.tuple2[T,S])
|
||||
/** A fold on the static attributes of this and nested Initializes. */
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S
|
||||
}
|
||||
object Initialize
|
||||
{
|
||||
|
|
@ -411,10 +432,17 @@ trait Init[Scope]
|
|||
def settings = this :: Nil
|
||||
def definitive: Boolean = !init.dependencies.contains(key)
|
||||
def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key)
|
||||
@deprecated("Will be made private.", "0.13.2")
|
||||
def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos)
|
||||
@deprecated("Will be made private.", "0.13.2")
|
||||
def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = (init validateReferenced g).right.map(newI => make(key, newI, pos))
|
||||
|
||||
private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] =
|
||||
(init validateKeyReferenced g).right.map(newI => make(key, newI, pos))
|
||||
|
||||
def mapKey(g: MapScoped): Setting[T] = make(g(key), init, pos)
|
||||
def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key,t)), pos)
|
||||
@deprecated("Will be made private.", "0.13.2")
|
||||
def mapConstant(g: MapConstant): Setting[T] = make(key, init mapConstant g, pos)
|
||||
def withPos(pos: SourcePosition) = make(key, init, pos)
|
||||
def positionString: Option[String] = pos match {
|
||||
|
|
@ -450,8 +478,8 @@ trait Init[Scope]
|
|||
new (ValidatedInit ~> Initialize) { def apply[T](v: ValidatedInit[T]) = handleUndefined[T](v) }
|
||||
|
||||
// mainly for reducing generated class count
|
||||
private[this] def validateReferencedT(g: ValidateRef) =
|
||||
new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateReferenced g }
|
||||
private[this] def validateKeyReferencedT(g: ValidateKeyRef) =
|
||||
new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateKeyReferenced g }
|
||||
|
||||
private[this] def mapReferencedT(g: MapScoped) =
|
||||
new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapReferenced g }
|
||||
|
|
@ -472,7 +500,7 @@ trait Init[Scope]
|
|||
final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform)
|
||||
final def evaluate(ss: Settings[Scope]): T = transform(getValue(ss, scopedKey))
|
||||
final def mapReferenced(g: MapScoped): Initialize[T] = new GetValue( g(scopedKey), transform)
|
||||
final def validateReferenced(g: ValidateRef): ValidatedInit[T] = g(scopedKey) match {
|
||||
private[sbt] final def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] = g(scopedKey, false) match {
|
||||
case Left(un) => Left(un :: Nil)
|
||||
case Right(nk) => Right(new GetValue(nk, transform))
|
||||
}
|
||||
|
|
@ -480,11 +508,13 @@ trait Init[Scope]
|
|||
case None => this
|
||||
case Some(const) => new Value(() => transform(const))
|
||||
}
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||
}
|
||||
private[this] final class GetValue[S,T](val scopedKey: ScopedKey[S], val transform: S => T) extends Keyed[S, T]
|
||||
trait KeyedInitialize[T] extends Keyed[T, T] {
|
||||
final val transform = idFun[T]
|
||||
}
|
||||
|
||||
private[sbt] final class TransformCapture(val f: Initialize ~> Initialize) extends Initialize[Initialize ~> Initialize]
|
||||
{
|
||||
def dependencies = Nil
|
||||
|
|
@ -492,7 +522,21 @@ trait Init[Scope]
|
|||
def evaluate(ss: Settings[Scope]): Initialize ~> Initialize = f
|
||||
def mapReferenced(g: MapScoped) = new TransformCapture(mapReferencedT(g) ∙ f)
|
||||
def mapConstant(g: MapConstant) = new TransformCapture(mapConstantT(g) ∙ f)
|
||||
def validateReferenced(g: ValidateRef) = Right(new TransformCapture(getValidated ∙ validateReferencedT(g) ∙ f))
|
||||
def validateKeyReferenced(g: ValidateKeyRef) = Right(new TransformCapture(getValidated ∙ validateKeyReferencedT(g) ∙ f))
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||
}
|
||||
private[sbt] final class ValidationCapture[T](val key: ScopedKey[T], val selfRefOk: Boolean) extends Initialize[ScopedKey[T]] {
|
||||
def dependencies = Nil
|
||||
def apply[Z](g2: ScopedKey[T] => Z): Initialize[Z] = map(this)(g2)
|
||||
def evaluate(ss: Settings[Scope]) = key
|
||||
def mapReferenced(g: MapScoped) = new ValidationCapture(g(key), selfRefOk)
|
||||
def mapConstant(g: MapConstant) = this
|
||||
def validateKeyReferenced(g: ValidateKeyRef) = g(key, selfRefOk) match {
|
||||
case Left(un) => Left(un :: Nil)
|
||||
case Right(k) => Right(new ValidationCapture(k, selfRefOk))
|
||||
}
|
||||
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||
}
|
||||
private[sbt] final class Bind[S,T](val f: S => Initialize[T], val in: Initialize[S]) extends Initialize[T]
|
||||
{
|
||||
|
|
@ -500,42 +544,49 @@ trait Init[Scope]
|
|||
def apply[Z](g: T => Z): Initialize[Z] = new Bind[S,Z](s => f(s)(g), in)
|
||||
def evaluate(ss: Settings[Scope]): T = f(in evaluate ss) evaluate ss
|
||||
def mapReferenced(g: MapScoped) = new Bind[S,T](s => f(s) mapReferenced g, in mapReferenced g)
|
||||
def validateReferenced(g: ValidateRef) = (in validateReferenced g).right.map { validIn =>
|
||||
new Bind[S,T](s => handleUndefined( f(s) validateReferenced g), validIn)
|
||||
def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map { validIn =>
|
||||
new Bind[S,T](s => handleUndefined( f(s) validateKeyReferenced g), validIn)
|
||||
}
|
||||
def mapConstant(g: MapConstant) = new Bind[S,T](s => f(s) mapConstant g, in mapConstant g)
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = in.processAttributes(init)(f)
|
||||
}
|
||||
private[sbt] final class Optional[S,T](val a: Option[Initialize[S]], val f: Option[S] => T) extends Initialize[T]
|
||||
{
|
||||
def dependencies = deps(a.toList)
|
||||
def apply[Z](g: T => Z): Initialize[Z] = new Optional[S,Z](a, g compose f)
|
||||
def mapReferenced(g: MapScoped) = new Optional(a map mapReferencedT(g).fn, f)
|
||||
def validateReferenced(g: ValidateRef) = a match {
|
||||
def validateKeyReferenced(g: ValidateKeyRef) = a match {
|
||||
case None => Right(this)
|
||||
case Some(i) => Right( new Optional(i.validateReferenced(g).right.toOption, f) )
|
||||
case Some(i) => Right( new Optional(i.validateKeyReferenced(g).right.toOption, f) )
|
||||
}
|
||||
def mapConstant(g: MapConstant): Initialize[T] = new Optional(a map mapConstantT(g).fn, f)
|
||||
def evaluate(ss: Settings[Scope]): T = f( a.flatMap( i => trapBadRef(evaluateT(ss)(i)) ) )
|
||||
// proper solution is for evaluate to be deprecated or for external use only and a new internal method returning Either be used
|
||||
private[this] def trapBadRef[A](run: => A): Option[A] = try Some(run) catch { case e: InvalidReference => None }
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = a match {
|
||||
case None => init
|
||||
case Some(i) => i.processAttributes(init)(f)
|
||||
}
|
||||
}
|
||||
private[sbt] final class Value[T](val value: () => T) extends Initialize[T]
|
||||
{
|
||||
def dependencies = Nil
|
||||
def mapReferenced(g: MapScoped) = this
|
||||
def validateReferenced(g: ValidateRef) = Right(this)
|
||||
def validateKeyReferenced(g: ValidateKeyRef) = Right(this)
|
||||
def apply[S](g: T => S) = new Value[S](() => g(value()))
|
||||
def mapConstant(g: MapConstant) = this
|
||||
def evaluate(map: Settings[Scope]): T = value()
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||
}
|
||||
private[sbt] final object StaticScopes extends Initialize[Set[Scope]]
|
||||
{
|
||||
def dependencies = Nil
|
||||
def mapReferenced(g: MapScoped) = this
|
||||
def validateReferenced(g: ValidateRef) = Right(this)
|
||||
def validateKeyReferenced(g: ValidateKeyRef) = Right(this)
|
||||
def apply[S](g: Set[Scope] => S) = map(this)(g)
|
||||
def mapConstant(g: MapConstant) = this
|
||||
def evaluate(map: Settings[Scope]) = map.scopes
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init
|
||||
}
|
||||
private[sbt] final class Apply[K[L[x]], T](val f: K[Id] => T, val inputs: K[Initialize], val alist: AList[K]) extends Initialize[T]
|
||||
{
|
||||
|
|
@ -545,13 +596,16 @@ trait Init[Scope]
|
|||
def mapConstant(g: MapConstant) = mapInputs( mapConstantT(g) )
|
||||
def mapInputs(g: Initialize ~> Initialize): Initialize[T] = new Apply(f, alist.transform(inputs, g), alist)
|
||||
def evaluate(ss: Settings[Scope]) = f(alist.transform(inputs, evaluateT(ss)))
|
||||
def validateReferenced(g: ValidateRef) =
|
||||
def validateKeyReferenced(g: ValidateKeyRef) =
|
||||
{
|
||||
val tx = alist.transform(inputs, validateReferencedT(g))
|
||||
val tx = alist.transform(inputs, validateKeyReferencedT(g))
|
||||
val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten)
|
||||
val get = new (ValidatedInit ~> Initialize) { def apply[T](vr: ValidatedInit[T]) = vr.right.get }
|
||||
if(undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs)
|
||||
}
|
||||
|
||||
private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S =
|
||||
(init /: alist.toList(inputs)) { (v, i) => i.processAttributes(v)(f) }
|
||||
}
|
||||
private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue