Derived settings: handle scopeLocal in derive and allow triggering dependencies to be filtered

This commit is contained in:
Mark Harrah 2013-05-09 17:13:22 -04:00
parent 19c78ac413
commit 61decef972
1 changed files with 32 additions and 20 deletions

View File

@ -77,9 +77,9 @@ trait Init[Scope]
* is explicitly defined and the where the scope matches `filter`. * is explicitly defined and the where the scope matches `filter`.
* A setting initialized with dynamic dependencies is only allowed if `allowDynamic` is true. * 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. */
final def derive[T](s: Setting[T], allowDynamic: Boolean = false, filter: Scope => Boolean = const(true)): Setting[T] = { 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 deriveAllowed(s, allowDynamic) foreach error
new DerivedSetting[T](s.key, s.init, s.pos, filter, nextDefaultID()) new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger, nextDefaultID())
} }
def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] = s.init match { def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] = s.init match {
case _: Bind[_,_] if !allowDynamic => Some("Cannot derive from dynamic dependencies.") case _: Bind[_,_] if !allowDynamic => Some("Cannot derive from dynamic dependencies.")
@ -115,11 +115,10 @@ trait Init[Scope]
{ {
val initDefaults = applyDefaults(init) val initDefaults = applyDefaults(init)
// inject derived settings into scopes where their dependencies are directly defined // inject derived settings into scopes where their dependencies are directly defined
val derived = derive(initDefaults) // and prepend per-scope settings
// prepend per-scope settings val derived = deriveAndLocal(initDefaults)
val withLocal = addLocal(derived)(scopeLocal)
// group by Scope/Key, dropping dead initializations // group by Scope/Key, dropping dead initializations
val sMap: ScopedMap = grouped(withLocal) val sMap: ScopedMap = grouped(derived)
// delegate references to undefined values according to 'delegates' // delegate references to undefined values according to 'delegates'
val dMap: ScopedMap = if(actual) delegate(sMap)(delegates, display) else sMap val dMap: ScopedMap = if(actual) delegate(sMap)(delegates, display) else sMap
// merge Seq[Setting[_]] into Compiled // merge Seq[Setting[_]] into Compiled
@ -266,12 +265,13 @@ trait Init[Scope]
} else "" } else ""
} }
private[this] def derive(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope]): Seq[Setting[_]] = private[this] def deriveAndLocal(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal): Seq[Setting[_]] =
{ {
import collection.mutable import collection.mutable
final class Derived(val setting: DerivedSetting[_]) { final class Derived(val setting: DerivedSetting[_]) {
val dependencies = setting.dependencies.map(_.key) val dependencies = setting.dependencies.map(_.key)
def triggeredBy = dependencies.filter(setting.trigger)
val inScopes = new mutable.HashSet[Scope] val inScopes = new mutable.HashSet[Scope]
} }
final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) { final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) {
@ -281,7 +281,8 @@ trait Init[Scope]
} }
// separate `derived` settings from normal settings (`defs`) // separate `derived` settings from normal settings (`defs`)
val (derived, defs) = Util.separate[Setting[_],Derived,Setting[_]](init) { case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) } val (derived, rawDefs) = Util.separate[Setting[_],Derived,Setting[_]](init) { case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) }
val defs = addLocal(rawDefs)(scopeLocal)
// group derived settings by the key they define // group derived settings by the key they define
val derivsByDef = new mutable.HashMap[AttributeKey[_], Deriveds] val derivsByDef = new mutable.HashMap[AttributeKey[_], Deriveds]
@ -297,7 +298,7 @@ trait Init[Scope]
// index derived settings by triggering key. This maps a key to the list of settings potentially derived from it. // index derived settings by triggering key. This maps a key to the list of settings potentially derived from it.
val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]] val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]]
for(s <- derived; d <- s.dependencies) for(s <- derived; d <- s.triggeredBy)
derivedBy.getOrElseUpdate(d, new mutable.ListBuffer) += s derivedBy.getOrElseUpdate(d, new mutable.ListBuffer) += s
// set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present // set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present
@ -310,19 +311,29 @@ trait Init[Scope]
delegates(scope).exists(s => defined.contains(ScopedKey(s, key))) delegates(scope).exists(s => defined.contains(ScopedKey(s, key)))
// true iff all dependencies of derived setting `d` have a value (potentially via delegation) in `scope` // true iff all dependencies of derived setting `d` have a value (potentially via delegation) in `scope`
def allDepsDefined(d: Derived, scope: Scope): Boolean = d.dependencies.forall(dep => isDefined(dep, scope)) def allDepsDefined(d: Derived, scope: Scope, local: Set[AttributeKey[_]]): Boolean =
d.dependencies.forall(dep => local(dep) || isDefined(dep, scope))
// list of injectable derived settings for `sk`. A derived setting is injectable if: // List of injectable derived settings and their local settings for `sk`.
// A derived setting is injectable if:
// 1. it has not been previously injected into this scope // 1. it has not been previously injected into this scope
// 2. it applies to this scope (as determined by its `filter`) // 2. it applies to this scope (as determined by its `filter`)
// 3. all of its dependencies are defined for that scope (allowing for delegation) // 3. all of its dependencies that match `trigger` are defined for that scope (allowing for delegation)
// This needs to handle local settings because a derived setting wouldn't be injected if it's local setting didn't exist yet.
val deriveFor = (sk: ScopedKey[_]) => { val deriveFor = (sk: ScopedKey[_]) => {
val derivedForKey: List[Derived] = derivedBy.get(sk.key).toList.flatten val derivedForKey: List[Derived] = derivedBy.get(sk.key).toList.flatten
val scope = sk.scope val scope = sk.scope
val filtered = derivedForKey.filter(d => d.inScopes.add(scope) && d.setting.filter(scope) && allDepsDefined(d, scope)) def localAndDerived(d: Derived): Seq[Setting[_]] =
val scoped = filtered.map(_.setting setScope scope) if(d.inScopes.add(scope) && d.setting.filter(scope))
addDefs(scoped) {
scoped val local = d.dependencies.flatMap(dep => scopeLocal(ScopedKey(scope, dep)))
if(allDepsDefined(d, scope, local.map(_.key.key).toSet))
local :+ d.setting.setScope(scope)
else
Nil
}
else Nil
derivedForKey.flatMap(localAndDerived)
} }
val processed = new mutable.HashSet[ScopedKey[_]] val processed = new mutable.HashSet[ScopedKey[_]]
@ -335,6 +346,7 @@ trait Init[Scope]
val sk = s.key val sk = s.key
val ds = if(processed.add(sk)) deriveFor(sk) else Nil val ds = if(processed.add(sk)) deriveFor(sk) else Nil
out ++= ds out ++= ds
addDefs(ds)
process(ds ::: ss) process(ds ::: ss)
case Nil => case Nil =>
} }
@ -397,8 +409,8 @@ trait Init[Scope]
protected[sbt] def isDerived: Boolean = false protected[sbt] def isDerived: Boolean = false
private[sbt] def setScope(s: Scope): Setting[T] = make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos) private[sbt] def setScope(s: Scope): Setting[T] = make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos)
} }
private[Init] final class DerivedSetting[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, val filter: Scope => Boolean, id: Long) extends DefaultSetting[T](sk, i, p, id) { private[Init] final class DerivedSetting[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, val filter: Scope => Boolean, val trigger: AttributeKey[_] => Boolean, id: Long) extends DefaultSetting[T](sk, i, p, id) {
override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new DerivedSetting[T](key, init, pos, filter, id) override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new DerivedSetting[T](key, init, pos, filter, trigger, id)
protected[sbt] override def isDerived: Boolean = true protected[sbt] override def isDerived: Boolean = true
} }
// Only keep the first occurence of this setting and move it to the front so that it has lower precedence than non-defaults. // Only keep the first occurence of this setting and move it to the front so that it has lower precedence than non-defaults.