2010-09-08 20:29:00 +02:00
|
|
|
package sbt
|
|
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
import Types._
|
2010-12-13 03:33:32 +01:00
|
|
|
import annotation.tailrec
|
2010-12-29 22:07:17 +01:00
|
|
|
import collection.mutable
|
2010-12-13 03:33:32 +01:00
|
|
|
|
|
|
|
|
sealed trait Settings[Scope]
|
|
|
|
|
{
|
|
|
|
|
def data: Scope => AttributeMap
|
2010-12-29 22:07:17 +01:00
|
|
|
def scopes: Seq[Scope]
|
2010-12-13 03:33:32 +01:00
|
|
|
def get[T](scope: Scope, key: AttributeKey[T]): Option[T]
|
|
|
|
|
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope]
|
|
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
|
|
|
|
|
private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope]
|
2010-09-08 20:29:00 +02:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
def scopes: Seq[Scope] = data.keys.toSeq
|
|
|
|
|
|
2010-12-13 03:33:32 +01:00
|
|
|
def get[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
2010-12-29 22:07:17 +01:00
|
|
|
delegates(scope).toStream.flatMap(sc => scopeLocal(sc, key) ).headOption
|
2010-12-13 03:33:32 +01:00
|
|
|
|
|
|
|
|
private def scopeLocal[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
|
|
|
|
(data get scope).flatMap(_ get key)
|
|
|
|
|
|
|
|
|
|
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] =
|
|
|
|
|
{
|
|
|
|
|
val map = (data get scope) getOrElse AttributeMap.empty
|
|
|
|
|
val newData = data.updated(scope, map.put(key, value))
|
2010-12-29 22:07:17 +01:00
|
|
|
new Settings0(newData, delegates)
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
2010-09-08 20:29:00 +02:00
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
// delegates should contain the input Scope as the first entry
|
|
|
|
|
final class Init[Scope](val delegates: Scope => Seq[Scope])
|
2010-09-08 20:29:00 +02:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
final case class ScopedKey[T](scope: Scope, key: AttributeKey[T])
|
2010-09-08 20:29:00 +02:00
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
type SettingSeq[T] = Seq[Setting[T]]
|
|
|
|
|
type ScopedMap = IMap[ScopedKey, SettingSeq]
|
|
|
|
|
type CompiledMap = Map[ScopedKey[_], Compiled]
|
|
|
|
|
type MapScoped = ScopedKey ~> ScopedKey
|
2010-12-13 03:33:32 +01:00
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
def value[T](key: ScopedKey[T])(value: => T): Setting[T] = new Value(key, value _)
|
|
|
|
|
def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = app(key, key :^: KNil)(h => f(h.head))
|
|
|
|
|
def app[HL <: HList, T](key: ScopedKey[T], inputs: KList[ScopedKey, HL])(f: HL => T): Setting[T] = new Apply(key, f, inputs)
|
2010-12-13 03:33:32 +01:00
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
def empty: Settings[Scope] = new Settings0(Map.empty, delegates)
|
|
|
|
|
def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) {
|
|
|
|
|
def apply[T](k: ScopedKey[T]): T = s.get(k.scope, k.key).get
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
def make(init: Seq[Setting[_]]): Settings[Scope] =
|
2010-12-13 03:33:32 +01:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
// group by Scope/Key, dropping dead initializations
|
|
|
|
|
val sMap: ScopedMap = grouped(init)
|
|
|
|
|
// delegate references to undefined values according to 'delegates'
|
|
|
|
|
val dMap: ScopedMap = delegate(sMap)
|
|
|
|
|
// merge Seq[Setting[_]] into Compiled
|
|
|
|
|
val cMap: CompiledMap = compile(dMap)
|
|
|
|
|
// order the initializations. cyclic references are detected here.
|
|
|
|
|
val ordered: Seq[Compiled] = sort(cMap)
|
|
|
|
|
// evaluation: apply the initializations.
|
|
|
|
|
applyInits(ordered)
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
def sort(cMap: CompiledMap): Seq[Compiled] =
|
|
|
|
|
Dag.topologicalSort(cMap.values)(_.dependencies.map(cMap))
|
|
|
|
|
|
|
|
|
|
def compile(sMap: ScopedMap): CompiledMap =
|
|
|
|
|
sMap.toSeq.map { case (k, ss) =>
|
|
|
|
|
val deps = ss flatMap { _.dependsOn }
|
|
|
|
|
val eval = (settings: Settings[Scope]) => (settings /: ss)(applySetting)
|
|
|
|
|
(k, new Compiled(deps, eval))
|
2010-12-13 03:33:32 +01:00
|
|
|
} toMap;
|
|
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
def grouped(init: Seq[Setting[_]]): ScopedMap =
|
|
|
|
|
((IMap.empty : ScopedMap) /: init) ( (m,s) => add(m,s) )
|
|
|
|
|
|
|
|
|
|
def add[T](m: ScopedMap, s: Setting[T]): ScopedMap =
|
|
|
|
|
m.mapValue[T]( s.key, Nil, ss => append(ss, s))
|
|
|
|
|
|
|
|
|
|
def append[T](ss: Seq[Setting[T]], s: Setting[T]): Seq[Setting[T]] =
|
|
|
|
|
if(s.definitive) s :: Nil else ss :+ s
|
|
|
|
|
|
|
|
|
|
def delegate(sMap: ScopedMap): ScopedMap =
|
2010-12-13 03:33:32 +01:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
val md = memoDelegates
|
|
|
|
|
def refMap(refKey: ScopedKey[_]) = new (ScopedKey ~> ScopedKey) { def apply[T](k: ScopedKey[T]) = mapReferenced(sMap, k, md(k.scope), refKey) }
|
|
|
|
|
val f = new (SettingSeq ~> SettingSeq) { def apply[T](ks: Seq[Setting[T]]) = ks.map{ s => s mapReferenced refMap(s.key) } }
|
|
|
|
|
sMap mapValues f
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
private[this] def mapReferenced[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], refKey: ScopedKey[_]): ScopedKey[T] =
|
|
|
|
|
{
|
|
|
|
|
val scache = PMap.empty[ScopedKey, ScopedKey]
|
|
|
|
|
def resolve(search: Seq[Scope]): ScopedKey[T] =
|
|
|
|
|
search match {
|
|
|
|
|
case Seq() => throw new Uninitialized(k)
|
|
|
|
|
case Seq(x, xs @ _*) =>
|
|
|
|
|
val sk = ScopedKey(x, k.key)
|
|
|
|
|
scache.getOrUpdate(sk, if(defines(sMap, sk, refKey)) sk else resolve(xs))
|
|
|
|
|
}
|
|
|
|
|
resolve(scopes)
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
private[this] def defines(map: ScopedMap, key: ScopedKey[_], refKey: ScopedKey[_]): Boolean =
|
|
|
|
|
(map get key) match { case Some(Seq(x, _*)) => (refKey != key) || x.definitive; case _ => false }
|
|
|
|
|
|
|
|
|
|
private[this] def applyInits(ordered: Seq[Compiled]): Settings[Scope] =
|
|
|
|
|
(empty /: ordered){ (m, comp) => comp.eval(m) }
|
2010-12-13 03:33:32 +01:00
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
private[this] def memoDelegates: Scope => Seq[Scope] =
|
2010-12-13 03:33:32 +01:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
val dcache = new mutable.HashMap[Scope, Seq[Scope]]
|
|
|
|
|
(scope: Scope) => dcache.getOrElseUpdate(scope, delegates(scope))
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
|
|
|
|
|
private[this] def applySetting[T](map: Settings[Scope], a: Setting[T]): Settings[Scope] =
|
2010-12-13 03:33:32 +01:00
|
|
|
a match
|
2010-09-08 20:29:00 +02:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
case s: Value[T] => map.set(s.key.scope, s.key.key, s.value())
|
|
|
|
|
case a: Apply[hl, T] => map.set(a.key.scope, a.key.key, a.f(a.inputs.down(asTransform(map))))
|
2010-09-08 20:29:00 +02:00
|
|
|
}
|
2010-12-13 03:33:32 +01:00
|
|
|
|
2010-12-29 22:07:17 +01:00
|
|
|
final class Uninitialized(key: ScopedKey[_]) extends Exception("Update on uninitialized setting " + key.key.label + " (in " + key.scope + ")")
|
|
|
|
|
final class Compiled(val dependencies: Iterable[ScopedKey[_]], val eval: Settings[Scope] => Settings[Scope])
|
|
|
|
|
|
2010-12-13 03:33:32 +01:00
|
|
|
sealed trait Setting[T]
|
|
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
def key: ScopedKey[T]
|
|
|
|
|
def definitive: Boolean
|
|
|
|
|
def dependsOn: Seq[ScopedKey[_]]
|
|
|
|
|
def mapReferenced(f: MapScoped): Setting[T]
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
private[this] final class Value[T](val key: ScopedKey[T], val value: () => T) extends Setting[T]
|
2010-12-13 03:33:32 +01:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
def definitive = true
|
|
|
|
|
def dependsOn = Nil
|
|
|
|
|
def mapReferenced(f: MapScoped) = this
|
2010-09-08 20:29:00 +02:00
|
|
|
}
|
2010-12-29 22:07:17 +01:00
|
|
|
private[this] final class Apply[HL <: HList, T](val key: ScopedKey[T], val f: HL => T, val inputs: KList[ScopedKey, HL]) extends Setting[T]
|
2010-12-13 03:33:32 +01:00
|
|
|
{
|
2010-12-29 22:07:17 +01:00
|
|
|
def definitive = !inputs.toList.contains(key)
|
|
|
|
|
def dependsOn = inputs.toList - key
|
|
|
|
|
def mapReferenced(g: MapScoped) = new Apply(key, f, inputs transform g)
|
2010-12-13 03:33:32 +01:00
|
|
|
}
|
|
|
|
|
}
|