speed up startup

This commit is contained in:
Mark Harrah 2011-04-27 20:40:52 -04:00
parent 5e9cc7ea5c
commit babf642dfc
6 changed files with 132 additions and 38 deletions

View File

@ -22,7 +22,7 @@ object Load
import BuildPaths._
import BuildStreams._
// note that there is State is passed in but not pulled out
// note that there is State passed in but not pulled out
def defaultLoad(state: State, baseDirectory: File, log: Logger): (() => Eval, BuildStructure) =
{
val provider = state.configuration.provider
@ -33,7 +33,7 @@ object Load
val classpath = provider.mainClasspath ++ scalaProvider.jars
val compilers = Compiler.compilers(state.configuration, log)
val evalPluginDef = EvaluateTask.evalPluginDef(log) _
val delegates = memo(defaultDelegates)
val delegates = defaultDelegates
val inject: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings
val rawConfig = new LoadBuildConfiguration(stagingDirectory, Nil, classpath, loader, compilers, evalPluginDef, delegates, EvaluateTask.injectStreams, inject, log)
val commonPlugins = buildGlobalPlugins(defaultGlobalPlugins, state, rawConfig)
@ -46,7 +46,9 @@ object Load
val rootProject = getRootProject(lb.units)
def resolveRef(project: Reference): ResolvedReference = Scope.resolveReference(lb.root, rootProject, project)
Scope.delegates(
lb.allProjectRefs,
resolveRef,
rootProject,
project => projectInherit(lb, project),
(project, config) => configInherit(lb, project, config, rootProject),
(project, task) => Nil,
@ -62,13 +64,7 @@ object Load
def configInheritRef(lb: LoadedBuild, ref: ProjectRef, config: ConfigKey): Seq[ConfigKey] =
configurationOpt(lb.units, ref.build, ref.project, config).toList.flatMap(_.extendsConfigs).map(c => ConfigKey(c.name))
def projectInherit(lb: LoadedBuild, ref: ResolvedReference): Seq[ProjectRef] =
ref match
{
case pr: ProjectRef => projectInheritRef(lb, pr)
case BuildRef(uri) => Nil
}
def projectInheritRef(lb: LoadedBuild, ref: ProjectRef): Seq[ProjectRef] =
def projectInherit(lb: LoadedBuild, ref: ProjectRef): Seq[ProjectRef] =
getProject(lb.units, ref.build, ref.project).delegates
// build, load, and evaluate all units.
@ -414,6 +410,21 @@ object Load
}
final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit])
{
checkCycles(units)
def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] = for( (uri, unit) <- units.toSeq; (id, proj) <- unit.defined ) yield ProjectRef(uri, id) -> proj
}
def checkCycles(units: Map[URI, LoadedBuildUnit])
{
def getRef(pref: ProjectRef) = units(pref.build).defined(pref.project)
def deps(proj: ResolvedProject)(base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] = Dag.topologicalSort(proj)(p => base(p) map getRef)
// check for cycles
for( (_, lbu) <- units; proj <- lbu.defined.values) {
deps(proj)(_.dependencies.map(_.project))
deps(proj)(_.delegates)
deps(proj)(_.aggregate)
}
}
final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit])
sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] }
final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase

View File

@ -94,6 +94,8 @@ object Project extends Init[Scope] with ProjectExtra
lazy val aggregate = aggregate0
lazy val dependencies = dependencies0
lazy val delegates = delegates0
Dag.topologicalSort(configurations)(_.extendsConfigs) // checks for cyclic references here instead of having to do it in Scope.delegates
}
def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil, delegates: => Seq[ProjectReference] = Nil,
@ -158,7 +160,7 @@ object Project extends Init[Scope] with ProjectExtra
def setCond[T](key: AttributeKey[T], vopt: Option[T], attributes: AttributeMap): AttributeMap =
vopt match { case Some(v) => attributes.put(key, v); case None => attributes.remove(key) }
def makeSettings(settings: Seq[Setting[_]], delegates: Scope => Seq[Scope], scopeLocal: ScopedKey[_] => Seq[Setting[_]]) =
translateUninitialized( make(settings)(delegates, scopeLocal) )
translateCyclic( make(settings)(delegates, scopeLocal) )
def display(scoped: ScopedKey[_]): String = Scope.display(scoped.scope, scoped.key.label)
def display(ref: Reference): String =
@ -197,14 +199,8 @@ object Project extends Init[Scope] with ProjectExtra
val f = mapScope(g)
ss.map(_ mapReferenced f)
}
def translateUninitialized[T](f: => T): T =
try { f } catch {
case u: Project.Uninitialized =>
val msg = "Uninitialized reference to " + display(u.key) + " from " + display(u.refKey)
throw new Uninitialized(u.key, u.refKey, msg)
case c: Dag.Cyclic =>
throw new MessageOnlyException(c.getMessage)
}
def translateCyclic[T](f: => T): T =
try { f } catch { case c: Dag.Cyclic => throw new MessageOnlyException(c.getMessage) }
def delegates(structure: BuildStructure, scope: Scope, key: AttributeKey[_]): Seq[ScopedKey[_]] =
structure.delegates(scope).map(d => ScopedKey(d, key))

View File

@ -117,10 +117,23 @@ object Scope
// *Inherit functions should be immediate delegates and not include argument itself. Transitivity will be provided by this method
def delegates(
refs: Seq[(ProjectRef, ResolvedProject)],
resolve: Reference => ResolvedReference,
projectInherit: ResolvedReference => Seq[ResolvedReference],
rootProject: URI => String,
projectInherit: ProjectRef => Seq[ProjectRef],
configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey],
taskInherit: (ResolvedReference, AttributeKey[_]) => Seq[AttributeKey[_]],
extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap]): Scope => Seq[Scope] =
{
val index = delegates(refs, projectInherit, configInherit)
scope => indexedDelegates(resolve, index, rootProject, taskInherit, extraInherit)(scope)
}
def indexedDelegates(
resolve: Reference => ResolvedReference,
index: DelegateIndex,
rootProject: URI => String,
taskInherit: (ResolvedReference, AttributeKey[_]) => Seq[AttributeKey[_]],
extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap])(rawScope: Scope): Seq[Scope] =
{
val scope = Scope.replaceThis(GlobalScope)(rawScope)
@ -128,9 +141,10 @@ object Scope
def nonProjectScopes(resolvedProj: ResolvedReference)(px: ScopeAxis[ResolvedReference]) =
{
val p = px.toOption getOrElse resolvedProj
val cLin = linearize(scope.config)(configInherit(p, _))
val tLin = linearize(scope.task)(taskInherit(p,_))
val eLin = linearize(scope.extra)(extraInherit(p,_))
val configProj = p match { case pr: ProjectRef => pr; case br: BuildRef => ProjectRef(br.build, rootProject(br.build)) }
val cLin = scope.config match { case Select(conf) => index.config(configProj, conf); case _ => withGlobalAxis(scope.config) }
val tLin = withGlobalAxis(scope.task)
val eLin = withGlobalAxis(scope.extra)
for(c <- cLin; t <- tLin; e <- eLin) yield Scope(px, c, t, e)
}
scope.project match
@ -139,24 +153,56 @@ object Scope
case This => withGlobalScope(scope.copy(project = Global))
case Select(proj) =>
val resolvedProj = resolve(proj)
val prod = withRawBuilds(linearize(scope.project map resolve, Nil)(projectInherit)) flatMap nonProjectScopes(resolvedProj)
(prod :+ GlobalScope).distinct
val projAxes: Seq[ScopeAxis[ResolvedReference]] =
resolvedProj match
{
case pr: ProjectRef => index.project(pr)
case br: BuildRef => Select(br) :: Global :: Nil
}
projAxes flatMap nonProjectScopes(resolvedProj)
}
}
def withGlobalAxis[T](base: ScopeAxis[T]): Seq[ScopeAxis[T]] = if(base.isSelect) base :: Global :: Nil else Global :: Nil
def withGlobalScope(base: Scope): Seq[Scope] = if(base == GlobalScope) GlobalScope :: Nil else base :: GlobalScope :: Nil
def withRawBuilds(ps: Seq[ScopeAxis[ResolvedReference]]): Seq[ScopeAxis[ResolvedReference]] =
(ps ++ (ps flatMap rawBuilds).map(Select.apply) :+ Global).distinct
def withRawBuilds(ps: Seq[ScopeAxis[ProjectRef]]): Seq[ScopeAxis[ResolvedReference]] =
ps ++ (ps flatMap rawBuild) :+ Global
def rawBuilds(ps: ScopeAxis[ResolvedReference]): Seq[ResolvedReference] = ps match { case Select(ref) => rawBuilds(ref); case _ => Nil }
def rawBuilds(ps: ResolvedReference): Seq[ResolvedReference] = (Reference.uri(ps) map BuildRef.apply).toList
def rawBuild(ps: ScopeAxis[ProjectRef]): Seq[ScopeAxis[BuildRef]] = ps match { case Select(ref) => Select(BuildRef(ref.build)) :: Nil; case _ => Nil }
def linearize[T](axis: ScopeAxis[T], append: Seq[ScopeAxis[T]] = Global :: Nil)(inherit: T => Seq[T]): Seq[ScopeAxis[T]] =
def delegates(
refs: Seq[(ProjectRef, ResolvedProject)],
projectInherit: ProjectRef => Seq[ProjectRef],
configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]): DelegateIndex =
{
val pDelegates = refs map { case (ref, project) =>
(ref, delegateIndex(ref, project.configurations)(projectInherit, configInherit) )
} toMap ;
new DelegateIndex0(pDelegates)
}
private[this] def delegateIndex(ref: ProjectRef, confs: Seq[Configuration])(projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]): ProjectDelegates =
{
val refDelegates = withRawBuilds(linearize(Select(ref), false)(projectInherit))
val configs = confs map { c => axisDelegates(configInherit, ref, ConfigKey(c.name)) }
val tasks = confs map { c => axisDelegates(configInherit, ref, ConfigKey(c.name)) }
new ProjectDelegates(ref, refDelegates, configs.toMap)
}
def axisDelegates[T](direct: (ResolvedReference, T) => Seq[T], ref: ResolvedReference, init: T): (T, Seq[ScopeAxis[T]]) =
( init, linearize(Select(init))(direct(ref, _)) )
def linearize[T](axis: ScopeAxis[T], appendGlobal: Boolean = true)(inherit: T => Seq[T]): Seq[ScopeAxis[T]] =
axis match
{
case Select(x) => (Dag.topologicalSort(x)(inherit).map(Select.apply).reverse ++ append).distinct
case Global | This => append
case Select(x) => topologicalSort(x, appendGlobal)(inherit)
case Global | This => if(appendGlobal) Global :: Nil else Nil
}
def topologicalSort[T](node: T, appendGlobal: Boolean)(dependencies: T => Seq[T]): Seq[ScopeAxis[T]] =
{
val o = Dag.topologicalSortUnchecked(node)(dependencies).map(Select.apply)
if(appendGlobal) o ::: Global :: Nil else o
}
}
@ -186,4 +232,22 @@ final case class ConfigKey(name: String)
object ConfigKey
{
implicit def configurationToKey(c: Configuration): ConfigKey = ConfigKey(c.name)
}
}
sealed trait DelegateIndex
{
def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]]
def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]]
// def task(ref: ProjectRef, task: ScopedKey[_]): Seq[ScopeAxis[ScopedKey[_]]]
// def extra(ref: ProjectRef, e: AttributeMap): Seq[ScopeAxis[AttributeMap]]
}
private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) extends DelegateIndex
{
def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match { case Some(pd) => pd.refs; case None => Nil }
def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] =
refs.get(ref) match {
case Some(pd) => pd.confs.get(conf) match { case Some(cs) => cs; case None => Nil }
case None => Nil
}
}
private final class ProjectDelegates(val ref: ProjectRef, val refs: Seq[ScopeAxis[ResolvedReference]], val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]])

View File

@ -36,6 +36,24 @@ object Dag
finished.toList;
}
// doesn't check for cycles
def topologicalSortUnchecked[T](node: T)(dependencies: T => Iterable[T]): List[T] =
{
val discovered = new mutable.HashSet[T]
var finished: List[T] = Nil
def visitAll(nodes: Iterable[T]) = nodes foreach visit
def visit(node : T){
if (!discovered(node)) {
discovered(node) = true;
visitAll(dependencies(node))
finished ::= node;
}
}
visit(node);
finished;
}
final class Cyclic(val value: Any, val all: List[Any], val complete: Boolean)
extends Exception( "Cyclic reference involving " + (if(complete) all.mkString(", ") else value) )
{

View File

@ -12,6 +12,8 @@ trait RMap[K[_], V[_]]
def get[T](k: K[T]): Option[V[T]]
def contains[T](k: K[T]): Boolean
def toSeq: Seq[(K[_], V[_])]
def keys: Iterable[K[_]]
def values: Iterable[V[_]]
}
trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K,V]
@ -54,6 +56,8 @@ object IMap
def mapValues[V2[_]](f: V ~> V2) =
new IMap0[K,V2](backing.mapValues(x => f(x)).toMap)
def toSeq = backing.toSeq
def keys = backing.keys
def values = backing.values
override def toString = backing.toString
}
@ -83,6 +87,9 @@ class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) extends Abstr
v
}
def toSeq = backing.toSeq
def keys = backing.keys
def values = backing.values
private[this] def cast[T](v: V[_]): V[T] = v.asInstanceOf[V[T]]
private[this] def cast[T](o: Option[V[_]]): Option[V[T]] = o map cast[T]

View File

@ -68,7 +68,7 @@ trait Init[Scope]
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 getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key) getOrElse error("Internal settings error: invalid reference to " + display(k))
def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k)
def compiled(init: Seq[Setting[_]], actual: Boolean = true)(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal): CompiledMap =
@ -124,18 +124,16 @@ trait Init[Scope]
}
private[this] def delegateForKey[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], refKey: ScopedKey[_], isFirst: Boolean): ScopedKey[T] =
{
val scache = PMap.empty[ScopedKey, ScopedKey]
def resolve(search: Seq[Scope]): ScopedKey[T] =
search match {
case Seq() => throw Uninitialized(k, refKey)
case Seq(x, xs @ _*) =>
val sk = ScopedKey(x, k.key)
scache.getOrUpdate(sk, if(defines(sMap, sk, refKey, isFirst)) sk else resolve(xs))
val definesKey = (refKey != sk || !isFirst) && (sMap contains sk)
if(definesKey) sk else resolve(xs)
}
resolve(scopes)
}
private[this] def defines(map: ScopedMap, key: ScopedKey[_], refKey: ScopedKey[_], isFirst: Boolean): Boolean =
(map get key) match { case Some(Seq(x, _*)) => (refKey != key) || !isFirst; case _ => false }
private[this] def applyInits(ordered: Seq[Compiled])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
(empty /: ordered){ (m, comp) => comp.eval(m) }
@ -149,7 +147,7 @@ trait Init[Scope]
final class Uninitialized(val key: ScopedKey[_], val refKey: ScopedKey[_], msg: String) extends Exception(msg)
def Uninitialized(key: ScopedKey[_], refKey: ScopedKey[_]): Uninitialized =
new Uninitialized(key, refKey, "Reference to uninitialized setting " + key.key.label + " (in " + key.scope + ") from " + refKey.key.label +" (in " + refKey.scope + ")")
new Uninitialized(key, refKey, "Reference to uninitialized setting " + display(key) + " from " + display(refKey))
final class Compiled(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]], val eval: Settings[Scope] => Settings[Scope])
{
override def toString = display(key)