merge ProjectContainer into Project, rearrange sub project methods

This commit is contained in:
Mark Harrah 2010-09-06 00:15:20 -04:00
parent 323115e263
commit 0c12c5e2bd
4 changed files with 102 additions and 59 deletions

View File

@ -242,7 +242,7 @@ object Commands
def loadProject = Command.simple(LoadProject, LoadProjectBrief, LoadProjectDetailed) { (in, s) =>
val base = s.configuration.baseDirectory
val p = MultiProject.load(s.configuration, ConsoleLogger() /*TODO*/)(base)
val exts = p match { case pc: ProjectContainer => MultiProject.loadExternals(pc :: Nil, p.info.construct); case _ => Map.empty[File, Project] }
val exts = MultiProject.loadExternals(p :: Nil, p.info.construct)
s.copy(project = p)().put(MultiProject.ExternalProjects, exts.updated(base, p))
}

View File

@ -7,6 +7,7 @@ import std._
import Path._
import TaskExtra._
import GlobFilter._
import Transform.Context
import inc.Analysis
import build.{Auto, Build}
import xsbti.AppConfiguration
@ -58,78 +59,96 @@ object MultiProject
Build.binaries(inputs.config.classpath, discovered, getClass.getClassLoader)(construct(info)).head.asInstanceOf[Project]
}
def loadExternals[P](from: Iterable[ProjectContainer], loadImpl: File => P): Map[File, P] =
def loadExternals(from: Iterable[Project], loadImpl: File => Project): Map[File, Project] =
{
def load(loaded: Map[File, P], file: File): Map[File, P] =
def load(loaded: Map[File, Project], file: File): Map[File, Project] =
(loaded get file) match
{
case None => doLoad(loaded, file)
case Some(p) => loaded
}
def doLoad(loaded: Map[File, P], file: File): Map[File, P] =
def doLoad(loaded: Map[File, Project], file: File): Map[File, Project] =
{
val loadedProject = loadImpl(file)
val newMap = loaded.updated(file, loadedProject)
loadedProject match
{
case container: ProjectContainer => loadAll( externals(container :: Nil), loaded )
case _ => newMap
}
loadAll( externals(loadedProject :: Nil), newMap )
}
def loadAll(files: Set[File], loaded: Map[File, P]): Map[File, P] = (loaded /: files)(load)
def loadAll(files: Set[File], loaded: Map[File, Project]): Map[File, Project] = (loaded /: files)(load)
loadAll( externals(from) , Map.empty)
}
def externals(containers: Iterable[ProjectContainer]): Set[File] =
def externals(containers: Iterable[Project]): Set[File] =
{
def exts(containers: Iterable[ProjectContainer]): Iterable[File] =
containers flatMap { container => container.externalProjects ++ exts(container.internalProjects) }
def exts(containers: Iterable[Project]): Iterable[File] =
containers flatMap { container => externalProjects(container) ++ exts(internalProjects(container)) }
exts(containers).toSet
}
def externalProjects(p: Project) = p.aggregate._1 ++ p.dependencies._1.map(_.path)
def internalProjects(p: Project) = p.aggregate._2 ++ p.dependencies._2.map(_.project)
def internalTopologicalSort(root: Project): Seq[Project] =
Dag.topologicalSort(root)(internalProjects)
def topologicalSort(root: Project, state: State): Seq[Project] = topologicalSort(root)(externalMap(state))
def topologicalSort(root: Project)(implicit resolveExternal: File => Project): Seq[Project] =
Dag.topologicalSort(root) { p =>
(externalProjects(p) map resolveExternal) ++ internalProjects(p)
}
def externalMap(state: State): File => Project = state get MultiProject.ExternalProjects getOrElse Map.empty
def makeContext(root: Project, state: State) =
{
val allProjects = ProjectContainer.topologicalSort(root, state)
val names = allProjects.map { p: Project => (p, p.name) }.toMap
//val tasks = allProjects map { p =>
ReflectiveContext(root, names.get _)
val allProjects = topologicalSort(root, state)
val contexts = allProjects map { p => (p, ReflectiveContext(p, p.name)) }
val externals = externalMap(state)
def subs(f: Project => (Iterable[File], Iterable[Project])): Project => Iterable[Project] = p =>
{
val (ext, int) = f(p)
(ext map externals) ++ int
}
val deps = (p: Project) => {
val (dsI, dsE) = p.dependencies
(dsI.map(_.path), dsE.map(_.project))
}
MultiContext(contexts)(subs(_.aggregate), subs(deps) )
}
}
/*trait MultiContext[Owner] extends Context[Owner]
object MultiContext
{
def ownerForName(name: String): Option[Owner]
def allTasks(owner: Owner): Iterable[Task[_]]
def externalProject(base: File): Project
}*/
def identityMap[A,B](in: Iterable[(A,B)]): collection.Map[A,B] =
{
import collection.JavaConversions._
val map: collection.mutable.Map[A, B] = new java.util.IdentityHashMap[A,B]
for( (a,b) <- in) map(a) = b
map
}
def fun[A,B](map: collection.Map[A,B]): A => Option[B] = map get _
def apply[Owner <: AnyRef](contexts: Iterable[(Owner, Context[Owner])])(agg: Owner => Iterable[Owner], deps: Owner => Iterable[Owner]): Context[Owner] = new Context[Owner]
{
val ownerReverse = (for( (owner, context) <- contexts; name <- context.ownerName(owner).toList) yield (name, owner) ).toMap
val ownerMap = identityMap[Task[_],Owner]( for((owner, context) <- contexts; task <- context.allTasks(owner) ) yield (task, owner) )
val context = identityMap(contexts)
def subMap(f: Owner => Iterable[Owner]) = identityMap( contexts.map { case (o,_) => (o,f(o)) })
val aggMap = subMap(agg)
val depMap = subMap(deps)
trait ProjectContainer
{
/** All projects that are loaded from their base directory instead of being defined in this container's compilation set.
* This is for any contained projects, including execution and classpath dependencies. */
def externalProjects: Iterable[File]
/** All projects directly contained in this that are defined in this container's compilation set.
* This is for any contained projects, including execution and classpath dependencies, but not external projects. */
def internalProjects: Iterable[Project]
def allTasks(owner: Owner): Iterable[Task[_]] = context(owner).allTasks(owner)
def ownerForName(name: String): Option[Owner] = ownerReverse get name
val staticName: Task[_] => Option[String] = t => owner(t) flatMap { o => context(o).staticName(t) }
val ownerName = (o: Owner) => context(o).ownerName(o)
val owner = (t: Task[_]) => ownerMap.get(t)
val aggregate = (o: Owner) => (aggMap get o).toList.flatten
def dependencies(o: Owner): Iterable[Owner] = (depMap get o).toList.flatten
val static = (o: Owner, s: String) => context(o).static(o, s)
}
}
object ProjectContainer
{
def internalTopologicalSort(root: Project): Seq[Project] =
Dag.topologicalSort(root) { _.internalProjects }
def topologicalSort(root: Project, state: State): Seq[Project] = topologicalSort(root)(state get MultiProject.ExternalProjects getOrElse Map.empty)
def topologicalSort(root: Project)(implicit resolveExternal: File => Project): Seq[Project] =
Dag.topologicalSort(root) { p =>
(p.externalProjects map resolveExternal) ++ p.internalProjects
}
}
trait Project extends Tasked with ProjectContainer
trait Project extends Tasked
{
val info: ProjectInfo
def name: String = info.name.get
def name: String = info.name getOrElse "'name' not overridden"
def base = info.projectDirectory
def outputRootPath = base / "target"
@ -139,6 +158,10 @@ trait Project extends Tasked with ProjectContainer
def input = Dummy.In
def state = Dummy.State
// (external, internal)
def aggregate: (Iterable[File], Iterable[Project])
def dependencies: (Iterable[ExternalDependency], Iterable[ProjectDependency])
type Task[T] = sbt.Task[T]
def act(input: Input, state: State): Option[(Task[State], Execute.NodeView[Task])] =
{
@ -146,28 +169,46 @@ trait Project extends Tasked with ProjectContainer
val context = MultiProject.makeContext(this, state)
val dummies = new Transform.Dummies(In, State, Streams)
def name(t: Task[_]): String = context.staticName(t) getOrElse std.Streams.name(t)
val injected = new Transform.Injected( input, state, std.Streams(t => streamBase / name(t), (t, writer) => ConsoleLogger() ) )
context.forName(input.name) map { t => (t.merge.map(_ => state), Transform(dummies, injected, context) ) }
val actualStreams = std.Streams(t => context.owner(t).get.streamBase / name(t), (t, writer) => ConsoleLogger() )
val injected = new Transform.Injected( input, state, actualStreams )
context.static(this, input.name) map { t => (t.merge.map(_ => state), Transform(dummies, injected, context) ) }
}
def help: Seq[Help] = Nil
}
trait ReflectiveProject extends Project
{
import ReflectUtilities.allVals
private[this] def vals[T: Manifest] = allVals[T](this).map(_._2)
def aggregate: (Iterable[File], Iterable[Project]) = (vals[ExternalProject].map(_.path), vals[Project] )
/** All projects directly contained in this that are defined in this container's compilation set.
* This is for any contained projects, including execution and classpath dependencies, but not external projects. */
def dependencies: (Iterable[ExternalDependency], Iterable[ProjectDependency]) = (vals[ExternalDependency], vals[ProjectDependency])
}
trait ProjectConstructors
{
val info: ProjectInfo
def project(base: Path, name: String, deps: ProjectDependency*): Project
def project[P <: Project](path: Path, name: String, builderClass: Class[P], deps: ProjectDependency*): P
def project[P <: Project](path: Path, name: String, construct: ProjectInfo => P, deps: ProjectDependency*): P
def project(base: Path): ExternalProject = new ExternalProject(base)
def project(base: Path): ExternalProject = new ExternalProject(base.asFile)
implicit def defaultProjectDependency(p: Project): ProjectDependency = new ProjectDependency(p, None)
implicit def dependencyConstructor(p: Project): ProjectDependencyConstructor = new ProjectDependencyConstructor {
def %(conf: String) = new ProjectDependency(p, Some(conf))
}
implicit def extDependencyConstructor(p: ExternalProject): ExtProjectDependencyConstructor = new ExtProjectDependencyConstructor {
def %(conf: String) = new ExternalDependency(p.path, Some(conf))
}
}
final class ProjectDependency(val project: Project, val configuration: Option[String])
sealed trait ProjectDependencyConstructor {
def %(conf: String): ProjectDependency
}
final class ExternalProject(val path: Path)
sealed trait ExtProjectDependencyConstructor {
def %(conf: String): ExternalDependency
}
final class ExternalProject(val path: File)
final class ExternalDependency(val path: File, val configuration: Option[String])

View File

@ -23,11 +23,11 @@ trait SingleProject extends Tasked with PrintTask with TaskExtra with Types
def act(input: Input, state: State): Option[(Task[State], Execute.NodeView[Task])] =
{
import Dummy._
val context = ReflectiveContext(this, (x: SingleProject) => Some("project")) // TODO: project names
val context = ReflectiveContext(this, "project")
val dummies = new Transform.Dummies(In, State, Streams)
def name(t: Task[_]): String = context.staticName(t) getOrElse std.Streams.name(t)
val injected = new Transform.Injected( input, state, std.Streams(t => streamBase / name(t), (t, writer) => ConsoleLogger() ) )
context.forName(input.name) map { t => (t.merge.map(_ => state), Transform(dummies, injected, context) ) }
context.static(this, input.name) map { t => (t.merge.map(_ => state), Transform(dummies, injected, context) ) }
}
def help: Seq[Help] = Nil
@ -118,7 +118,7 @@ final class MapBackedAttribute[T](val default: Option[T], subs: Map[String, MapB
newSubs put (h, su
}
trait ConsoleLogManager
trait ConsoleLogManager extends LogManager
{
def makeLogger(context: Context, configuration: Configuration) = (task: Task[_], to: PrintWriter) =>
{
@ -144,18 +144,19 @@ trait ConsoleLogManager
object ReflectiveContext
{
import Transform.Context
def apply[Owner <: AnyRef : Manifest](context: Owner, name: Owner => Option[String]): Context[Owner] = new Context[Owner]
def apply[Owner <: AnyRef : Manifest](context: Owner, name: String): Context[Owner] = new Context[Owner]
{
private[sbt] lazy val tasks: Map[String, Task[_]] = ReflectUtilities.allVals[Task[_]](context).toMap
private[sbt] lazy val reverseName: collection.Map[Task[_], String] = reverseMap(tasks)
private[sbt] lazy val sub: collection.Map[String, Owner] = ReflectUtilities.allVals[Owner](context)
private[sbt] lazy val reverseSub: collection.Map[Owner, String] = reverseMap(sub)
def forName(s: String): Option[Task[_]] = tasks get s
val staticName: Task[_] => Option[String] = reverseName.get _
val ownerName = name
val owner = (_: Task[_]) => Some(context)
val subs = (o: Owner) => Nil
val ownerName = (o: Owner) => if(o eq context) Some(name) else None
val owner = (t: Task[_]) => if(reverseName contains t) Some(context) else None
def allTasks(o: Owner): Iterable[Task[_]] = if(o eq context) tasks.values else Nil
def ownerForName(oname: String): Option[Owner] = if(name == oname) Some(context) else None
val aggregate = (_: Owner) => Nil
val static = (o: Owner, s: String) => if(o eq context) tasks.get(s) else None
private def reverseMap[A,B](in: Iterable[(A,B)]): collection.Map[B,A] =

View File

@ -102,12 +102,13 @@ object Transform
final class Injected[Input, State](val in: Input, val state: State, val streams: Streams)
trait Context[Owner]
{
def forName(s: String): Option[Task[_]]
def staticName: Task[_] => Option[String]
def owner: Task[_] => Option[Owner]
def ownerName: Owner => Option[String]
def subs: Owner => Iterable[Owner]
def aggregate: Owner => Iterable[Owner]
def static: (Owner, String) => Option[Task[_]]
def allTasks(owner: Owner): Iterable[Task[_]]
def ownerForName(name: String): Option[Owner]
}
def setOriginal(delegate: Task ~> Task): Task ~> Task =
new (Task ~> Task) {
@ -129,7 +130,7 @@ object Transform
import System._
import Convert._
val inputs = dummyMap(dummyIn, dummyState)(in, state)
Convert.taskToNode setOriginal(streamed(streams, dummyStreams)) implied(owner, subs, static) setOriginal(name(staticName)) getOrId(inputs)
Convert.taskToNode setOriginal(streamed(streams, dummyStreams)) implied(owner, aggregate, static) setOriginal(name(staticName)) getOrId(inputs)
}
}
object Convert