multi-project model based on Settings and ProjectRef

This commit is contained in:
Mark Harrah 2011-01-18 18:24:11 -05:00
parent c24c0b7a23
commit bcc8c37f4d
3 changed files with 85 additions and 19 deletions

View File

@ -1,3 +1,6 @@
/* sbt -- Simple Build Tool
* Copyright 2011 Mark Harrah
*/
package sbt package sbt
import Types._ import Types._
@ -6,15 +9,17 @@ package sbt
sealed trait Settings[Scope] sealed trait Settings[Scope]
{ {
def data: Scope => AttributeMap def data: Map[Scope, AttributeMap]
def scopes: Seq[Scope] def keys(scope: Scope): Set[AttributeKey[_]]
def scopes: Set[Scope]
def get[T](scope: Scope, key: AttributeKey[T]): Option[T] def get[T](scope: Scope, key: AttributeKey[T]): Option[T]
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope]
} }
private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope] private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope]
{ {
def scopes: Seq[Scope] = data.keys.toSeq def scopes: Set[Scope] = data.keySet.toSet
def keys(scope: Scope) = data(scope).keys.toSet
def get[T](scope: Scope, key: AttributeKey[T]): Option[T] = def get[T](scope: Scope, key: AttributeKey[T]): Option[T] =
delegates(scope).toStream.flatMap(sc => scopeLocal(sc, key) ).headOption delegates(scope).toStream.flatMap(sc => scopeLocal(sc, key) ).headOption
@ -30,7 +35,8 @@ private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val del
} }
} }
// delegates should contain the input Scope as the first entry // delegates should contain the input Scope as the first entry
final class Init[Scope](val delegates: Scope => Seq[Scope]) // this trait is intended to be mixed into an object
trait Init[Scope]
{ {
final case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) final case class ScopedKey[T](scope: Scope, key: AttributeKey[T])
@ -42,18 +48,28 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
def value[T](key: ScopedKey[T])(value: => T): Setting[T] = new Value(key, value _) 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 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) def app[HL <: HList, T](key: ScopedKey[T], inputs: KList[ScopedKey, HL])(f: HL => T): Setting[T] = new Apply(key, f, inputs)
def uniform[S,T](key: ScopedKey[T], inputs: Seq[ScopedKey[S]])(f: Seq[S] => T): Setting[T] = new Uniform(key, f, inputs)
def kapp[HL <: HList, M[_], T](key: ScopedKey[T], inputs: KList[({type l[t] = ScopedKey[M[t]]})#l, HL])(f: KList[M, HL] => T): Setting[T] = new KApply[HL, M, T](key, f, inputs)
def empty: Settings[Scope] = new Settings0(Map.empty, delegates) // the following is a temporary workaround for the "... cannot be instantiated from ..." bug, which renders 'kapp' above unusable outside this source file
def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) { class KApp[HL <: HList, M[_], T] {
def apply[T](k: ScopedKey[T]): T = s.get(k.scope, k.key).get type Composed[S] = ScopedKey[M[S]]
def apply(key: ScopedKey[T], inputs: KList[Composed, HL])(f: KList[M, HL] => T): Setting[T] = new KApply[HL, M, T](key, f, inputs)
} }
def make(init: Seq[Setting[_]]): Settings[Scope] = def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = new Settings0(Map.empty, delegates)
def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) {
def apply[T](k: ScopedKey[T]): T = getValue(s, k)
}
def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key).get
def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k)
def make(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
{ {
// group by Scope/Key, dropping dead initializations // group by Scope/Key, dropping dead initializations
val sMap: ScopedMap = grouped(init) val sMap: ScopedMap = grouped(init)
// delegate references to undefined values according to 'delegates' // delegate references to undefined values according to 'delegates'
val dMap: ScopedMap = delegate(sMap) val dMap: ScopedMap = delegate(sMap)(delegates)
// merge Seq[Setting[_]] into Compiled // merge Seq[Setting[_]] into Compiled
val cMap: CompiledMap = compile(dMap) val cMap: CompiledMap = compile(dMap)
// order the initializations. cyclic references are detected here. // order the initializations. cyclic references are detected here.
@ -80,9 +96,9 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
def append[T](ss: Seq[Setting[T]], s: Setting[T]): Seq[Setting[T]] = def append[T](ss: Seq[Setting[T]], s: Setting[T]): Seq[Setting[T]] =
if(s.definitive) s :: Nil else ss :+ s if(s.definitive) s :: Nil else ss :+ s
def delegate(sMap: ScopedMap): ScopedMap = def delegate(sMap: ScopedMap)(implicit delegates: Scope => Seq[Scope]): ScopedMap =
{ {
val md = memoDelegates val md = memoDelegates(delegates)
def refMap(refKey: ScopedKey[_]) = new (ScopedKey ~> ScopedKey) { def apply[T](k: ScopedKey[T]) = mapReferenced(sMap, k, md(k.scope), refKey) } 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) } } val f = new (SettingSeq ~> SettingSeq) { def apply[T](ks: Seq[Setting[T]]) = ks.map{ s => s mapReferenced refMap(s.key) } }
sMap mapValues f sMap mapValues f
@ -102,21 +118,27 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
private[this] def defines(map: ScopedMap, key: ScopedKey[_], refKey: ScopedKey[_]): Boolean = 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 } (map get key) match { case Some(Seq(x, _*)) => (refKey != key) || x.definitive; case _ => false }
private[this] def applyInits(ordered: Seq[Compiled]): Settings[Scope] = private[this] def applyInits(ordered: Seq[Compiled])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
(empty /: ordered){ (m, comp) => comp.eval(m) } (empty /: ordered){ (m, comp) => comp.eval(m) }
private[this] def memoDelegates: Scope => Seq[Scope] = private[this] def memoDelegates(implicit delegates: Scope => Seq[Scope]): Scope => Seq[Scope] =
{ {
val dcache = new mutable.HashMap[Scope, Seq[Scope]] val dcache = new mutable.HashMap[Scope, Seq[Scope]]
(scope: Scope) => dcache.getOrElseUpdate(scope, delegates(scope)) (scope: Scope) => dcache.getOrElseUpdate(scope, delegates(scope))
} }
private[this] def applySetting[T](map: Settings[Scope], a: Setting[T]): Settings[Scope] = private[this] def applySetting[T](map: Settings[Scope], setting: Setting[T]): Settings[Scope] =
a match {
def execK[HL <: HList, M[_]](a: KApply[HL, M, T]) =
map.set(a.key.scope, a.key.key, a.f(a.inputs.transform[M]( nestCon[ScopedKey, Id, M](asTransform(map)) )) )
setting match
{ {
case s: Value[T] => map.set(s.key.scope, s.key.key, s.value()) 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)))) case u: Uniform[s, T] => map.set(u.key.scope, u.key.key, u.f(u.inputs map asFunction(map)) )
case a: Apply[hl, T] => map.set(a.key.scope, a.key.key, a.f(a.inputs down asTransform(map) ) )
case ka: KApply[hl, m, T] => execK[hl, m](ka) // separate method needed to workaround bug where m is not recognized as higher-kinded in inline version
} }
}
final class Uninitialized(key: ScopedKey[_]) extends Exception("Update on uninitialized setting " + key.key.label + " (in " + key.scope + ")") 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]) final class Compiled(val dependencies: Iterable[ScopedKey[_]], val eval: Settings[Scope] => Settings[Scope])
@ -126,18 +148,37 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
def key: ScopedKey[T] def key: ScopedKey[T]
def definitive: Boolean def definitive: Boolean
def dependsOn: Seq[ScopedKey[_]] def dependsOn: Seq[ScopedKey[_]]
def mapReferenced(f: MapScoped): Setting[T] def mapReferenced(g: MapScoped): Setting[T]
def mapKey(g: MapScoped): Setting[T]
} }
private[this] final class Value[T](val key: ScopedKey[T], val value: () => T) extends Setting[T] private[this] final class Value[T](val key: ScopedKey[T], val value: () => T) extends Setting[T]
{ {
def definitive = true def definitive = true
def dependsOn = Nil def dependsOn = Nil
def mapReferenced(f: MapScoped) = this def mapReferenced(g: MapScoped) = this
def mapKey(g: MapScoped): Setting[T] = new Value(g(key), value)
} }
private[this] final class Apply[HL <: HList, T](val key: ScopedKey[T], val f: HL => T, val inputs: KList[ScopedKey, HL]) extends Setting[T] private[this] final class Apply[HL <: HList, T](val key: ScopedKey[T], val f: HL => T, val inputs: KList[ScopedKey, HL]) extends Setting[T]
{ {
def definitive = !inputs.toList.contains(key) def definitive = !inputs.toList.contains(key)
def dependsOn = inputs.toList - key def dependsOn = remove(inputs.toList, key)
def mapReferenced(g: MapScoped) = new Apply(key, f, inputs transform g) def mapReferenced(g: MapScoped) = new Apply(key, f, inputs transform g)
def mapKey(g: MapScoped): Setting[T] = new Apply(g(key), f, inputs)
} }
private[this] final class KApply[HL <: HList, M[_], T](val key: ScopedKey[T], val f: KList[M, HL] => T, val inputs: KList[({type l[t] = ScopedKey[M[t]]})#l, HL]) extends Setting[T]
{
def definitive = !inputs.toList.contains(key)
def dependsOn = remove(unnest(inputs.toList), key)
def mapReferenced(g: MapScoped) = new KApply[HL, M, T](key, f, inputs.transform[({type l[t] = ScopedKey[M[t]]})#l]( nestCon(g) ) )
def mapKey(g: MapScoped): Setting[T] = new KApply[HL, M, T](g(key), f, inputs)
private[this] def unnest(l: List[ScopedKey[M[T]] forSome { type T }]): List[ScopedKey[_]] = l.asInstanceOf[List[ScopedKey[_]]]
}
private[this] final class Uniform[S, T](val key: ScopedKey[T], val f: Seq[S] => T, val inputs: Seq[ScopedKey[S]]) extends Setting[T]
{
def definitive = !inputs.contains(key)
def dependsOn = remove(inputs, key)
def mapReferenced(g: MapScoped) = new Uniform(key, f, inputs map g.fn[S])
def mapKey(g: MapScoped): Setting[T] = new Uniform(g(key), f, inputs)
}
private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v)
} }

View File

@ -8,16 +8,24 @@ trait TypeFunctions
type Id[X] = X type Id[X] = X
sealed trait Const[A] { type Apply[B] = A } sealed trait Const[A] { type Apply[B] = A }
sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] } sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] }
sealed trait [A[_], B[_]] { type l[T] = A[B[T]] }
sealed trait P1of2[M[_,_], A] { type Apply[B] = M[A,B]; type Flip[B] = M[B, A] } sealed trait P1of2[M[_,_], A] { type Apply[B] = M[A,B]; type Flip[B] = M[B, A] }
final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) } final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) }
final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) } final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) }
final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) } final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) }
def nestCon[M[_], N[_], G[_]](f: M ~> N): (M G)#l ~> (N G)#l =
f.asInstanceOf[(M G)#l ~> (N G)#l] // implemented with a cast to avoid extra object+method call. castless version:
/* new ( (M ∙ G)#l ~> (N ∙ G)#l ) {
def apply[T](mg: M[G[T]]): N[G[T]] = f(mg)
}*/
implicit def toFn1[A,B](f: A => B): Fn1[A,B] = new Fn1[A,B] { implicit def toFn1[A,B](f: A => B): Fn1[A,B] = new Fn1[A,B] {
def [C](g: C => A) = f compose g def [C](g: C => A) = f compose g
} }
type Endo[T] = T=>T
type ~>|[A[_],B[_]] = A ~> Compose[Option, B]#Apply type ~>|[A[_],B[_]] = A ~> Compose[Option, B]#Apply
} }
object TypeFunctions extends TypeFunctions object TypeFunctions extends TypeFunctions

View File

@ -0,0 +1,17 @@
/* sbt -- Simple Build Tool
* Copyright 2011 Mark Harrah
*/
package sbt
object Collections
{
def separate[T,A,B](ps: Seq[T])(f: T => Either[A,B]): (Seq[A], Seq[B]) =
((Nil: Seq[A], Nil: Seq[B]) /: ps)( (xs, y) => prependEither(xs, f(y)) )
def prependEither[A,B](acc: (Seq[A], Seq[B]), next: Either[A,B]): (Seq[A], Seq[B]) =
next match
{
case Left(l) => (l +: acc._1, acc._2)
case Right(r) => (acc._1, r +: acc._2)
}
}