mirror of https://github.com/sbt/sbt.git
project loading
This commit is contained in:
parent
cc4fc95dba
commit
6b4844094a
|
|
@ -166,6 +166,10 @@ CompileSyntax + """
|
||||||
def LoadCommand = "load-commands"
|
def LoadCommand = "load-commands"
|
||||||
def LoadCommandLabel = "commands"
|
def LoadCommandLabel = "commands"
|
||||||
|
|
||||||
|
def LoadProject = "loadp"
|
||||||
|
def LoadProjectBrief = (LoadProject, LoadProjectDetailed)
|
||||||
|
def LoadProjectDetailed = "Loads the project in the current directory"
|
||||||
|
|
||||||
def Shell = "shell"
|
def Shell = "shell"
|
||||||
def ShellBrief = (Shell, ShellDetailed)
|
def ShellBrief = (Shell, ShellDetailed)
|
||||||
def ShellDetailed = "Provides an interactive prompt from which commands can be run."
|
def ShellDetailed = "Provides an interactive prompt from which commands can be run."
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ package sbt
|
||||||
|
|
||||||
object Compile
|
object Compile
|
||||||
{
|
{
|
||||||
|
val DefaultMaxErrors = 100
|
||||||
|
|
||||||
final class Inputs(val compilers: Compilers, val config: Options, val incSetup: IncSetup, val log: Logger)
|
final class Inputs(val compilers: Compilers, val config: Options, val incSetup: IncSetup, val log: Logger)
|
||||||
final class Options(val classpath: Seq[File], val sources: Seq[File], val classesDirectory: File, val options: Seq[String], val javacOptions: Seq[String], val maxErrors: Int)
|
final class Options(val classpath: Seq[File], val sources: Seq[File], val classesDirectory: File, val options: Seq[String], val javacOptions: Seq[String], val maxErrors: Int)
|
||||||
final class IncSetup(val javaSrcBases: Seq[File], val cacheDirectory: File)
|
final class IncSetup(val javaSrcBases: Seq[File], val cacheDirectory: File)
|
||||||
|
|
@ -31,6 +33,12 @@ object Compile
|
||||||
log
|
log
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def compilers(implicit app: AppConfiguration, log: Logger): Compilers =
|
||||||
|
{
|
||||||
|
val scalaProvider = app.provider.scalaProvider
|
||||||
|
compilers(ScalaInstance(scalaProvider.version, scalaProvider.launcher))
|
||||||
|
}
|
||||||
|
|
||||||
def compilers(instance: ScalaInstance)(implicit app: AppConfiguration, log: Logger): Compilers =
|
def compilers(instance: ScalaInstance)(implicit app: AppConfiguration, log: Logger): Compilers =
|
||||||
compilers(instance, ClasspathOptions.auto)
|
compilers(instance, ClasspathOptions.auto)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,13 @@ 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] }
|
||||||
|
s.copy(project = p)().put(MultiProject.ExternalProjects, exts.updated(base, p))
|
||||||
|
}
|
||||||
|
|
||||||
def handleException(e: Throwable, s: State, trace: Boolean = true): State = {
|
def handleException(e: Throwable, s: State, trace: Boolean = true): State = {
|
||||||
// TODO: log instead of print
|
// TODO: log instead of print
|
||||||
if(trace)
|
if(trace)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import std._
|
||||||
|
import Path._
|
||||||
|
import TaskExtra._
|
||||||
|
import GlobFilter._
|
||||||
|
import inc.Analysis
|
||||||
|
import build.{Auto, Build}
|
||||||
|
import xsbti.AppConfiguration
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object MultiProject
|
||||||
|
{
|
||||||
|
val ExternalProjects = AttributeKey[Map[File, Project]]("external-projects")
|
||||||
|
|
||||||
|
val defaultExcludes: FileFilter = (".*" - ".") || HiddenFileFilter
|
||||||
|
def descendents(base: PathFinder, select: FileFilter) = base.descendentsExcept(select, defaultExcludes)
|
||||||
|
def children(base: PathFinder, select: FileFilter) = base * (select -- defaultExcludes)
|
||||||
|
def projectClassName = classOf[Project].getName
|
||||||
|
|
||||||
|
def projectStandard(base: Path) = base / "project"
|
||||||
|
def projectHidden(base: Path) = base / ".sbt"
|
||||||
|
def selectProjectDir(base: Path) =
|
||||||
|
{
|
||||||
|
val a = projectHidden(base)
|
||||||
|
val b = projectStandard(base)
|
||||||
|
if(a.exists) a else b
|
||||||
|
}
|
||||||
|
|
||||||
|
def crossPath(base: Path, instance: ScalaInstance) = base / ("scala_" + instance.actualVersion)
|
||||||
|
|
||||||
|
def construct[T <: AnyRef](arg: T)(implicit mf: Manifest[T]): Class[_] => Any = clazz =>
|
||||||
|
Build.constructor(clazz, mf.erasure) match {
|
||||||
|
case Some(c) => c.newInstance(arg)
|
||||||
|
case None => error("Couldn't find constructor with one argument of type " + mf.toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
def load(configuration: AppConfiguration, log: Logger)(base: File): Project =
|
||||||
|
{
|
||||||
|
val projectDir = selectProjectDir(base)
|
||||||
|
val buildDir = projectDir / "build"
|
||||||
|
val srcMain = buildDir / "src" / "main"
|
||||||
|
val javaSrcBase = srcMain / "java"
|
||||||
|
val sources = children(buildDir +++ projectDir, "*.scala") +++ descendents(buildDir / "scala" +++ buildDir / "java", "*.scala" | "*.java")
|
||||||
|
val classpath = configuration.provider.mainClasspath.toSeq
|
||||||
|
val compilers = Compile.compilers(configuration, log)
|
||||||
|
val target = crossPath(buildDir / "target", compilers.scalac.scalaInstance)
|
||||||
|
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 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
def loadExternals[P](from: Iterable[ProjectContainer], loadImpl: File => P): Map[File, P] =
|
||||||
|
{
|
||||||
|
def load(loaded: Map[File, P], file: File): Map[File, P] =
|
||||||
|
(loaded get file) match
|
||||||
|
{
|
||||||
|
case None => doLoad(loaded, file)
|
||||||
|
case Some(p) => loaded
|
||||||
|
}
|
||||||
|
def doLoad(loaded: Map[File, P], file: File): Map[File, P] =
|
||||||
|
{
|
||||||
|
val loadedProject = loadImpl(file)
|
||||||
|
val newMap = loaded.updated(file, loadedProject)
|
||||||
|
loadedProject match
|
||||||
|
{
|
||||||
|
case container: ProjectContainer => loadAll( externals(container :: Nil), loaded )
|
||||||
|
case _ => newMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def loadAll(files: Set[File], loaded: Map[File, P]): Map[File, P] = (loaded /: files)(load)
|
||||||
|
|
||||||
|
loadAll( externals(from) , Map.empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
def externals(containers: Iterable[ProjectContainer]): Set[File] =
|
||||||
|
{
|
||||||
|
def exts(containers: Iterable[ProjectContainer]): Iterable[File] =
|
||||||
|
containers flatMap { container => container.externalProjects ++ exts(container.internalProjects) }
|
||||||
|
exts(containers).toSet
|
||||||
|
}
|
||||||
|
|
||||||
|
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 _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*trait MultiContext[Owner] extends Context[Owner]
|
||||||
|
{
|
||||||
|
def ownerForName(name: String): Option[Owner]
|
||||||
|
def allTasks(owner: Owner): Iterable[Task[_]]
|
||||||
|
def externalProject(base: File): Project
|
||||||
|
}*/
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
val info: ProjectInfo
|
||||||
|
def name: String = info.name.get
|
||||||
|
|
||||||
|
def base = info.projectDirectory
|
||||||
|
def outputRootPath = base / "target"
|
||||||
|
def streamBase = outputRootPath / "streams"
|
||||||
|
|
||||||
|
implicit def streams = Dummy.Streams
|
||||||
|
def input = Dummy.In
|
||||||
|
def state = Dummy.State
|
||||||
|
|
||||||
|
type Task[T] = sbt.Task[T]
|
||||||
|
def act(input: Input, state: State): Option[(Task[State], Execute.NodeView[Task])] =
|
||||||
|
{
|
||||||
|
import Dummy._
|
||||||
|
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) ) }
|
||||||
|
}
|
||||||
|
|
||||||
|
def help: Seq[Help] = Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final class ProjectDependency(val project: Project, val configuration: Option[String])
|
||||||
|
sealed trait ProjectDependencyConstructor {
|
||||||
|
def %(conf: String): ProjectDependency
|
||||||
|
}
|
||||||
|
final class ExternalProject(val path: Path)
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2008, 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import xsbti.{AppConfiguration, AppProvider, ScalaProvider}
|
||||||
|
import inc.Analysis
|
||||||
|
|
||||||
|
/** Represents the minimal information necessary to construct a Project.
|
||||||
|
*
|
||||||
|
* `projectDirectory` is the base directory for the project (not the root project directory)
|
||||||
|
* `builderPath` is the base directory for the project (not the root project directory)
|
||||||
|
* `dependencies` are the Projects that this Project depends on.
|
||||||
|
* `parent` is the parent Project, or None if this is the root project.
|
||||||
|
* `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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
object ProjectInfo
|
||||||
|
{
|
||||||
|
val MetadataDirectoryName = "project"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue