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
105127c122
commit
5dcd8bd913
|
|
@ -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