From c2a9a95baf78fe08a0409b348c2d1188a84c7cbc Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 12 Sep 2010 22:58:22 -0400 Subject: [PATCH] redo external projects move externals from State to ProjectInfo --- main/Main.scala | 8 +++++--- main/MultiProject.scala | 26 ++++++++++++-------------- main/ProjectInfo.scala | 10 +++++++++- main/Watched.scala | 6 +++--- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/main/Main.scala b/main/Main.scala index c0e0b1f96..3f3097d05 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -241,9 +241,11 @@ 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 = MultiProject.loadExternals(p :: Nil, p.info.construct) - s.copy(project = p)().put(MultiProject.ExternalProjects, exts.updated(base, p)).put(MultiProject.InitialProject, p) + lazy val p: Project = MultiProject.load(s.configuration, ConsoleLogger() /*TODO*/, ProjectInfo.externals(exts))(base) + // lazy so that p can forward-reference it + lazy val exts: Map[File, Project] = MultiProject.loadExternals(p :: Nil, p.info.construct).updated(base, p) + exts// force + runExitHooks(s).copy(project = p)().put(MultiProject.InitialProject, p) } def handleException(e: Throwable, s: State, trace: Boolean = true): State = { diff --git a/main/MultiProject.scala b/main/MultiProject.scala index d5b17887b..73b46687b 100644 --- a/main/MultiProject.scala +++ b/main/MultiProject.scala @@ -17,7 +17,6 @@ import annotation.tailrec object MultiProject { - val ExternalProjects = AttributeKey[Map[File, Project]]("external-projects") val InitialProject = AttributeKey[Project]("initial-project") val defaultExcludes: FileFilter = (".*" - ".") || HiddenFileFilter @@ -42,7 +41,8 @@ object MultiProject case None => error("Couldn't find constructor with one argument of type " + mf.toString) } - def load(configuration: AppConfiguration, log: Logger)(base: File): Project = + // externals must not be evaluated until after _all_ projects have been loaded + def load(configuration: AppConfiguration, log: Logger, externals: ExternalProjects)(base: File): Project = { val projectDir = selectProjectDir(base) val buildDir = projectDir / "build" @@ -55,7 +55,7 @@ object MultiProject val inputs = Compile.inputs(classpath, sources.getFiles.toSeq, target, Nil, Nil, javaSrcBase :: Nil, Compile.DefaultMaxErrors)(compilers, log) val analysis = Compile(inputs) - val info = ProjectInfo(None, base, projectDir, Nil, None)(configuration, analysis, inputs, load(configuration, log)) + val info = ProjectInfo(None, base, projectDir, Nil, None)(configuration, analysis, inputs, load(configuration, log, externals), externals) val discovered = Build.check(Build.discover(analysis, Some(false), Auto.Subclass, projectClassName), false) Build.binaries(inputs.config.classpath, discovered, getClass.getClassLoader)(construct(info)).head.asInstanceOf[Project] @@ -93,19 +93,17 @@ object MultiProject 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] = + def topologicalSort(root: Project): Seq[Project] = topologicalSort(root, root.info.externals) + def topologicalSort(root: Project, 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 initialProject(state: State, ifUnset: => Project): Project =state get MultiProject.InitialProject getOrElse ifUnset - def makeContext(root: Project, state: State) = + def makeContext(root: Project) = { - val allProjects = topologicalSort(root, state) - val contexts = allProjects map { p => (p, ReflectiveContext(p, p.name)) } - val externals = externalMap(state) + val contexts = topologicalSort(root) map { p => (p, ReflectiveContext(p, p.name)) } + val externals = root.info.externals def subs(f: Project => Iterable[ProjectDependency]): Project => Iterable[Project] = p => f(p) map( _.project match { case Left(path) => externals(path); case Right(proj) => proj } ) MultiContext(contexts)(subs(_.aggregate), subs(_.dependencies) ) @@ -157,7 +155,7 @@ trait Project extends Tasked with HistoryEnabled with Member[Project] with Named def historyPath = Some(outputRootPath / ".history") def streamBase = outputRootPath / "streams" - def projectClosure(s: State): Seq[Project] = MultiProject.topologicalSort(MultiProject.initialProject(s, rootProject), s) + def projectClosure(s: State): Seq[Project] = MultiProject.topologicalSort(MultiProject.initialProject(s, rootProject)) @tailrec final def rootProject: Project = info.parent match { case Some(p) => p.rootProject; case None => this } implicit def streams = Dummy.Streams @@ -171,7 +169,7 @@ trait Project extends Tasked with HistoryEnabled with Member[Project] with Named def act(input: Input, state: State): Option[(Task[State], Execute.NodeView[Task])] = { import Dummy._ - val context = MultiProject.makeContext(this, state) + val context = MultiProject.makeContext(this) val dummies = new Transform.Dummies(In, State, Streams) def name(t: Task[_]): String = context.staticName(t.original) getOrElse std.Streams.name(t) val mklog = LogManager.construct(context, settings) @@ -227,8 +225,8 @@ trait ProjectConstructors extends Project sealed trait ProjectDependency { val project: Either[File, Project] } object ProjectDependency { - final class Execution(val project: Either[File, Project]) extends ProjectDependency - final class Classpath(val project: Either[File, Project], configuration: Option[String]) extends ProjectDependency + final case class Execution(project: Either[File, Project]) extends ProjectDependency + final case class Classpath(project: Either[File, Project], configuration: Option[String]) extends ProjectDependency } sealed trait ProjectDependencyConstructor { def %(conf: String): ProjectDependency.Classpath diff --git a/main/ProjectInfo.scala b/main/ProjectInfo.scala index 29cec563f..e467e89b9 100644 --- a/main/ProjectInfo.scala +++ b/main/ProjectInfo.scala @@ -16,16 +16,24 @@ import inc.Analysis * `buildScalaVersion` contains the explicitly requested Scala version to use for building (as when using `+` or `++`) or None if the normal version should be used. */ final case class ProjectInfo(name: Option[String], projectDirectory: File, builderDir: File, dependencies: Iterable[Project], parent: Option[Project])( - val configuration: AppConfiguration, val analysis: Analysis, val compileInputs: Compile.Inputs, val construct: File => Project) + val configuration: AppConfiguration, val analysis: Analysis, val compileInputs: Compile.Inputs, val construct: File => Project, external: ExternalProjects) { def app = configuration.provider /** The version of Scala running sbt.*/ def definitionScalaVersion = app.scalaProvider.version /** The launcher instance that booted sbt.*/ def launcher = app.scalaProvider.launcher + def globalLock = launcher.globalLock + /* cannot be referenced in a Project's constructor */ + lazy val externals = external.value +} +final class ExternalProjects(externalMap: => Map[File, Project]) +{ + lazy val value = externalMap } object ProjectInfo { + def externals(externalMap: => Map[File, Project]) = new ExternalProjects(externalMap) val MetadataDirectoryName = "project" } \ No newline at end of file diff --git a/main/Watched.scala b/main/Watched.scala index de871341a..3b15f38b7 100644 --- a/main/Watched.scala +++ b/main/Watched.scala @@ -18,12 +18,12 @@ object Watched val ContinuousCompilePollDelaySeconds = 1 def isEnter(key: Int): Boolean = key == 10 || key == 13 - def watched(p: Project, s: State): Seq[Watched] = MultiProject.topologicalSort(p, s).collect { case w: Watched => w } - def sourcePaths(p: Project, s: State): PathFinder = (Path.emptyPathFinder /: watched(p, s))(_ +++ _.watchPaths) + def watched(p: Project): Seq[Watched] = MultiProject.topologicalSort(p).collect { case w: Watched => w } + def sourcePaths(p: Project): PathFinder = (Path.emptyPathFinder /: watched(p))(_ +++ _.watchPaths) def executeContinuously(project: Project with Watched, s: State, in: Input): State = { def shouldTerminate: Boolean = (System.in.available > 0) && (project.terminateWatch(System.in.read()) || shouldTerminate) - val sourcesFinder = sourcePaths(project, s) + val sourcesFinder = sourcePaths(project) val watchState = s get ContinuousState getOrElse WatchState.empty if(watchState.count > 0)