redo external projects

move externals from State to ProjectInfo
This commit is contained in:
Mark Harrah 2010-09-12 22:58:22 -04:00
parent e3d39175d4
commit c2a9a95baf
4 changed files with 29 additions and 21 deletions

View File

@ -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 = {

View File

@ -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

View File

@ -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"
}

View File

@ -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)