From 0c12c5e2bd43e7910b8be0b96eca800a62d1fb2a Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 6 Sep 2010 00:15:20 -0400 Subject: [PATCH] merge ProjectContainer into Project, rearrange sub project methods --- main/Main.scala | 2 +- main/MultiProject.scala | 135 +++++++++++++++++++++++------------- main/SingleProject.scala | 17 ++--- tasks/standard/System.scala | 7 +- 4 files changed, 102 insertions(+), 59 deletions(-) diff --git a/main/Main.scala b/main/Main.scala index d922f670a..22c6e0f80 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -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)) } diff --git a/main/MultiProject.scala b/main/MultiProject.scala index 5a190f241..1598facb5 100644 --- a/main/MultiProject.scala +++ b/main/MultiProject.scala @@ -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) \ No newline at end of file +sealed trait ExtProjectDependencyConstructor { + def %(conf: String): ExternalDependency +} +final class ExternalProject(val path: File) +final class ExternalDependency(val path: File, val configuration: Option[String]) \ No newline at end of file diff --git a/main/SingleProject.scala b/main/SingleProject.scala index a5d135f8a..98a88c966 100644 --- a/main/SingleProject.scala +++ b/main/SingleProject.scala @@ -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] = diff --git a/tasks/standard/System.scala b/tasks/standard/System.scala index 59ceb380a..775d3694e 100644 --- a/tasks/standard/System.scala +++ b/tasks/standard/System.scala @@ -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