mirror of https://github.com/sbt/sbt.git
Settings
This commit is contained in:
parent
05195ecc7c
commit
22a71f2432
|
|
@ -9,12 +9,12 @@ package sbt
|
|||
|
||||
object LogManager
|
||||
{
|
||||
def construct(context: Transform.Context[Project], settings: Settings) = (task: Task[_], to: PrintWriter) =>
|
||||
def construct(context: Transform.Context[Project]) = (task: Task[_], to: PrintWriter) =>
|
||||
{
|
||||
val owner = context owner task
|
||||
val ownerName = owner flatMap ( context ownerName _ ) getOrElse ""
|
||||
val taskPath = (context staticName task).toList ::: ownerName :: Nil
|
||||
def level(key: AttributeKey[Level.Value], default: Level.Value): Level.Value = settings.get(key, taskPath) getOrElse default
|
||||
def level(key: AttributeKey[Level.Value], default: Level.Value): Level.Value = default//settings.get(key, taskPath) getOrElse default
|
||||
val screenLevel = level(ScreenLogLevel, Level.Info)
|
||||
val backingLevel = level(PersistLogLevel, Level.Debug)
|
||||
|
||||
|
|
|
|||
|
|
@ -165,7 +165,6 @@ trait Project extends Tasked with HistoryEnabled with Member[Project] with Named
|
|||
{
|
||||
val info: ProjectInfo
|
||||
|
||||
def settings: Settings = Settings.empty
|
||||
def name: String = info.name getOrElse error("'name' not overridden")
|
||||
def normalizedName: String = StringUtilities.normalize(name)
|
||||
|
||||
|
|
@ -191,7 +190,7 @@ trait Project extends Tasked with HistoryEnabled with Member[Project] with Named
|
|||
val context = MultiProject.makeContext(this)
|
||||
val dummies = new Transform.Dummies(In, State, Streams, Context)
|
||||
def name(t: Task[_]): String = context.staticName(t.original) getOrElse std.Streams.name(t)
|
||||
val mklog = LogManager.construct(context, settings)
|
||||
val mklog = LogManager.construct(context)
|
||||
def getOwner(t: Task[_]) = context.owner(t.original).getOrElse(error("No owner for " + name(t.original) + "\n\t" + t.original))
|
||||
val actualStreams = std.Streams(t => getOwner(t).streamBase / name(t), mklog )
|
||||
val injected = new Transform.Injected( input, state, actualStreams )
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import Types._
|
|||
// a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts
|
||||
sealed trait AttributeKey[T] {
|
||||
def label: String
|
||||
override final def toString = label
|
||||
}
|
||||
object AttributeKey
|
||||
{
|
||||
|
|
@ -30,6 +31,9 @@ trait AttributeMap
|
|||
object AttributeMap
|
||||
{
|
||||
val empty: AttributeMap = new BasicAttributeMap(Map.empty)
|
||||
implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = new (AttributeKey ~> Id) {
|
||||
def apply[T](key: AttributeKey[T]): T = map(key)
|
||||
}
|
||||
}
|
||||
private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any]) extends AttributeMap
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,21 +14,29 @@ object Dag
|
|||
import scala.collection.{mutable, JavaConversions};
|
||||
import JavaConversions.{asIterable, asSet}
|
||||
|
||||
def topologicalSort[T](root: T)(dependencies: T => Iterable[T]) = {
|
||||
// TODO: replace implementation with call to new version
|
||||
def topologicalSort[T](root: T)(dependencies: T => Iterable[T]): List[T] = topologicalSort(root :: Nil)(dependencies)
|
||||
|
||||
def topologicalSort[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] =
|
||||
{
|
||||
val discovered = new mutable.HashSet[T]
|
||||
val finished = asSet(new java.util.LinkedHashSet[T])
|
||||
|
||||
def visitAll(nodes: Iterable[T]) = nodes foreach visit
|
||||
def visit(dag : T){
|
||||
if (!discovered(dag)) {
|
||||
discovered(dag) = true;
|
||||
dependencies(dag).foreach(visit);
|
||||
visitAll(dependencies(dag));
|
||||
finished += dag;
|
||||
}
|
||||
else if(!finished(dag))
|
||||
throw new Cyclic(dag)
|
||||
}
|
||||
|
||||
visit(root);
|
||||
visitAll(nodes);
|
||||
|
||||
finished.toList;
|
||||
}
|
||||
final class Cyclic(val value: Any) extends Exception("Cyclic reference involving " + value)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,136 @@
|
|||
package sbt
|
||||
|
||||
sealed trait Settings
|
||||
import annotation.tailrec
|
||||
import Settings._
|
||||
|
||||
sealed trait Settings[Scope]
|
||||
{
|
||||
def get[T](key: AttributeKey[T], path: List[String]): Option[T]
|
||||
def set[T](key: AttributeKey[T], path: List[String], value: T): Settings
|
||||
def data: Scope => AttributeMap
|
||||
def definitions: Scope => Definitions
|
||||
def linear: Scope => Seq[Scope]
|
||||
def get[T](scope: Scope, key: AttributeKey[T]): Option[T]
|
||||
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope]
|
||||
}
|
||||
private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val definitions: Map[Scope, Definitions], val linear: Scope => Seq[Scope]) extends Settings[Scope]
|
||||
{
|
||||
def get[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
||||
linear(scope).toStream.flatMap(sc => scopeLocal(sc, key) ).headOption
|
||||
|
||||
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))
|
||||
new Settings0(newData, definitions, linear)
|
||||
}
|
||||
}
|
||||
object Settings
|
||||
{
|
||||
def empty: Settings = new Basic(Map.empty)
|
||||
def x = 3
|
||||
type Data[Scope] = Map[Scope, AttributeMap]
|
||||
type Init = Seq[Setting[_]]
|
||||
type Keys = Set[AttributeKey[_]]
|
||||
|
||||
private[this] class Basic(val roots: Map[ List[String], AttributeMap ]) extends Settings
|
||||
def make[Scope](inits: Iterable[(Scope,Init)], lzA: Scope => Seq[Scope]): Settings[Scope] =
|
||||
{
|
||||
def get[T](key: AttributeKey[T], path: List[String]): Option[T] =
|
||||
{
|
||||
def notFound = path match {
|
||||
case Nil => None
|
||||
case x :: xs => get(key, xs)
|
||||
}
|
||||
(roots get path) flatMap ( _ get key ) orElse notFound
|
||||
}
|
||||
def set[T](key: AttributeKey[T], path: List[String], value: T): Settings =
|
||||
{
|
||||
val amap = (roots get path) getOrElse AttributeMap.empty
|
||||
new Basic( roots updated(path, amap put(key, value)) )
|
||||
}
|
||||
val definitions = inits map { case (scope, init) => (scope, compile(init)) } toMap;
|
||||
val resolved = for( (scope, definition) <- definitions) yield (scope, resolveScopes(definition, lzA(scope), definitions) )
|
||||
val scopeDeps = resolved map { case (scope, requiredMap) => (scope, requiredMap.values) } toMap;
|
||||
val ordered = Dag.topologicalSort(scopeDeps.keys)(scopeDeps)
|
||||
val data = (Map.empty[Scope, AttributeMap] /: ordered) { (mp, scope) => add(mp, scope, definitions, resolved) }
|
||||
new Settings0(data, definitions, lzA)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def add[Scope](data: Data[Scope], scope: Scope, definitions: Map[Scope, Definitions], resolved: Map[Scope, Map[AttributeKey[_], Scope]]): Map[Scope, AttributeMap] =
|
||||
data.updated(scope, mkScopeMap(data, definitions(scope), resolved(scope)) )
|
||||
|
||||
private[this] def mkScopeMap[Scope](data: Data[Scope], definitions: Definitions, definedIn: Map[AttributeKey[_], Scope]): AttributeMap =
|
||||
{
|
||||
val start = (AttributeMap.empty /: definitions.requires) ( (mp, key) => prepop(data, definedIn, mp, key))
|
||||
definitions eval start
|
||||
}
|
||||
|
||||
private[this] def prepop[T, Scope](data: Data[Scope], definedIn: Map[AttributeKey[_], Scope], mp: AttributeMap, key: AttributeKey[T]): AttributeMap =
|
||||
mp.put(key, data(definedIn(key))(key))
|
||||
|
||||
private[this] def resolveScopes[Scope](definition: Definitions, search: Seq[Scope], definitions: Map[Scope, Definitions]): Map[AttributeKey[_], Scope] =
|
||||
definition.requires.view.map(req => (req, resolveScope(req, search, definitions )) ).toMap
|
||||
|
||||
private[this] def resolveScope[Scope](key: AttributeKey[_], search: Seq[Scope], definitions: Map[Scope, Definitions]): Scope =
|
||||
search find defines(key, definitions) getOrElse { throw new Uninitialized(key) }
|
||||
|
||||
private[this] def defines[Scope](key: AttributeKey[_], definitions: Map[Scope, Definitions])(scope: Scope): Boolean =
|
||||
(definitions get scope).filter(_.provides contains key).isDefined
|
||||
|
||||
final class Definitions(val provides: Keys, val requires: Keys, val eval: AttributeMap => AttributeMap)
|
||||
|
||||
def value[T](key: AttributeKey[T])(value: => T): Setting[T] = new Value(key, value _)
|
||||
def update[T](key: AttributeKey[T])(f: T => T): Setting[T] = new Update(key, f)
|
||||
def app[HL <: HList, T](key: AttributeKey[T], inputs: KList[AttributeKey, HL])(f: HL => T): Setting[T] = new Apply(key, f, inputs)
|
||||
|
||||
def compile(settings: Seq[Setting[_]]): Definitions =
|
||||
{
|
||||
val grpd = grouped(settings)
|
||||
val sorted = sort(grpd)
|
||||
val eval = (map: AttributeMap) => (map /: sorted)( (m, c) => c eval m )
|
||||
val provided = grpd.keySet.toSet
|
||||
val requires = sorted.flatMap(_.dependencies).toSet -- provided ++ sorted.collect { case c if !c.selfContained => c.key }
|
||||
new Definitions(provided, requires, eval)
|
||||
}
|
||||
private[this] def grouped(settings: Seq[Setting[_]]): Map[AttributeKey[_], Compiled] =
|
||||
settings.groupBy(_.key) map { case (key: AttributeKey[t], actions) =>
|
||||
(key: AttributeKey[_], compileSetting(key, actions.asInstanceOf[Seq[Setting[t]]]) )
|
||||
} toMap;
|
||||
|
||||
private[this] def compileSetting[T](key: AttributeKey[T], actions: Seq[Setting[T]]): Compiled =
|
||||
{
|
||||
val (alive, selfContained) = live(key, actions)
|
||||
val f = (map: AttributeMap) => (map /: alive)(eval)
|
||||
new Compiled(key, f, dependencies(actions), selfContained)
|
||||
}
|
||||
private[this] final class Compiled(val key: AttributeKey[_], val eval: AttributeMap => AttributeMap, val dependencies: Iterable[AttributeKey[_]], val selfContained: Boolean) {
|
||||
override def toString = key.label
|
||||
}
|
||||
|
||||
private[this] def sort(actionMap: Map[AttributeKey[_], Compiled]): Seq[Compiled] =
|
||||
Dag.topologicalSort(actionMap.values)( _.dependencies.flatMap(actionMap.get) )
|
||||
|
||||
private[this] def live[T](key: AttributeKey[T], actions: Seq[Setting[T]]): (Seq[Setting[T]], Boolean) =
|
||||
{
|
||||
val lastOverwrite = actions.lastIndexWhere(_ overwrite key)
|
||||
val selfContained = lastOverwrite >= 0
|
||||
val alive = if(selfContained) actions.drop(lastOverwrite) else actions
|
||||
(alive, selfContained)
|
||||
}
|
||||
private[this] def dependencies(actions: Seq[Setting[_]]): Seq[AttributeKey[_]] = actions.flatMap(_.dependsOn)
|
||||
private[this] def eval[T](map: AttributeMap, a: Setting[T]): AttributeMap =
|
||||
a match
|
||||
{
|
||||
case s: Value[T] => map.put(s.key, s.value())
|
||||
case u: Update[T] => map.put(u.key, u.f(map(u.key)))
|
||||
case a: Apply[hl, T] => map.put(a.key, a.f(a.inputs.down(map)))
|
||||
}
|
||||
|
||||
sealed trait Setting[T]
|
||||
{
|
||||
def key: AttributeKey[T]
|
||||
def overwrite(key: AttributeKey[T]): Boolean
|
||||
def dependsOn: Seq[AttributeKey[_]] = Nil
|
||||
}
|
||||
private[this] final class Value[T](val key: AttributeKey[T], val value: () => T) extends Setting[T]
|
||||
{
|
||||
def overwrite(key: AttributeKey[T]) = true
|
||||
}
|
||||
private[this] final class Update[T](val key: AttributeKey[T], val f: T => T) extends Setting[T]
|
||||
{
|
||||
def overwrite(key: AttributeKey[T]) = false
|
||||
}
|
||||
private[this] final class Apply[HL <: HList, T](val key: AttributeKey[T], val f: HL => T, val inputs: KList[AttributeKey, HL]) extends Setting[T]
|
||||
{
|
||||
def overwrite(key: AttributeKey[T]) = inputs.toList.forall(_ ne key)
|
||||
override def dependsOn = inputs.toList - key
|
||||
}
|
||||
|
||||
final class Uninitialized(key: AttributeKey[_]) extends Exception("Update on uninitialized setting " + key.label)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue