mirror of https://github.com/sbt/sbt.git
133 lines
5.5 KiB
Scala
133 lines
5.5 KiB
Scala
/* sbt -- Simple Build Tool
|
|
* Copyright 2010 Mark Harrah
|
|
*/
|
|
package sbt
|
|
package build
|
|
|
|
import java.io.File
|
|
import classpath.ClasspathUtilities.toLoader
|
|
import ModuleUtilities.getObject
|
|
import compile.{AnalyzingCompiler, JavaCompiler}
|
|
import inc.Analysis
|
|
import Path._
|
|
import GlobFilter._
|
|
|
|
final class BuildException(msg: String) extends RuntimeException(msg)
|
|
|
|
object Build
|
|
{
|
|
def loader(configuration: xsbti.AppConfiguration): ClassLoader =
|
|
configuration.provider.mainClass.getClassLoader
|
|
|
|
def apply(command: LoadCommand, configuration: xsbti.AppConfiguration, allowMultiple: Boolean): Seq[Any] =
|
|
command match
|
|
{
|
|
case BinaryLoad(classpath, module, name) =>
|
|
binary(classpath, module, name, loader(configuration), allowMultiple)
|
|
case SourceLoad(classpath, sourcepath, output, module, auto, name) =>
|
|
source(classpath, sourcepath, output, module, auto, name, configuration, allowMultiple)
|
|
case ProjectLoad(base, auto, name) =>
|
|
project(base, auto, name, configuration, allowMultiple)
|
|
}
|
|
|
|
def project(base: File, auto: Auto.Value, name: String, configuration: xsbti.AppConfiguration, allowMultiple: Boolean): Seq[Any] =
|
|
{
|
|
val buildDir = base / "project" / "build"
|
|
val sources = (buildDir * "*.scala") +++ (buildDir / "src" / "main" / "scala" ** "*.scala")
|
|
source(Nil, sources.get.toSeq, Some(buildDir / "target" asFile), None, auto, name, configuration, allowMultiple)
|
|
}
|
|
|
|
def binary(classpath: Seq[File], module: Boolean, name: String, parent: ClassLoader, allowMultiple: Boolean): Seq[Any] =
|
|
{
|
|
if(name.isEmpty)
|
|
error("Class name required to load binary project.")
|
|
else
|
|
{
|
|
val names = if(allowMultiple) name.split(",").toSeq else Seq(name)
|
|
binaries(classpath, names.map(n => ToLoad(n,module)), parent)(_.newInstance)
|
|
}
|
|
}
|
|
|
|
def compile(command: CompileCommand, configuration: xsbti.AppConfiguration): Analysis =
|
|
{
|
|
import command._
|
|
compile(classpath, sources, output, options, configuration)
|
|
}
|
|
def compile(classpath: Seq[File], sources: Seq[File], output: Option[File], options: Seq[String], configuration: xsbti.AppConfiguration): Analysis =
|
|
compile(new Compile(classpath, sources, output, options, configuration))
|
|
|
|
def compile(conf : Compile): Analysis =
|
|
{
|
|
import conf._
|
|
// TODO: accept Logger as an argument
|
|
val log = ConsoleLogger()
|
|
|
|
val componentManager = new ComponentManager(launcher.globalLock, configuration.provider.components, log)
|
|
val compiler = new AnalyzingCompiler(instance, componentManager, log)
|
|
val javac = JavaCompiler.directOrFork(compiler.cp, compiler.scalaInstance)( (args: Seq[String], log: Logger) => Process("javac", args) ! log )
|
|
|
|
val agg = new AggressiveCompile(cacheDirectory)
|
|
agg(compiler, javac, sources, compileClasspath, outputDirectory, Nil, options)(log)
|
|
}
|
|
def source(classpath: Seq[File], sources: Seq[File], output: Option[File], module: Option[Boolean], auto: Auto.Value, name: String, configuration: xsbti.AppConfiguration, allowMultiple: Boolean = false): Seq[Any] =
|
|
{
|
|
val conf = new Compile(classpath, sources, output, Nil, configuration)
|
|
val analysis = compile(conf)
|
|
val discovered = discover(analysis, module, auto, name)
|
|
binaries(conf.projectClasspath, check(discovered, allowMultiple), loader(configuration))( constructCompiled(new Compiled(conf, analysis) ) )
|
|
}
|
|
def constructCompiled(compiled: Compiled): Class[_] => Any = clazz =>
|
|
constructor(clazz, classOf[Compiled]) match {
|
|
case Some(c) => c.newInstance(compiled)
|
|
case None => clazz.newInstance
|
|
}
|
|
|
|
def constructor(c: Class[_], args: Class[_]*): Option[java.lang.reflect.Constructor[_]] =
|
|
try { Some( c.getConstructor( args : _*) ) }
|
|
catch { case e: NoSuchMethodException => None }
|
|
|
|
def discover(analysis: inc.Analysis, module: Option[Boolean], auto: Auto.Value, name: String): Seq[ToLoad] =
|
|
{
|
|
import Auto.{Annotation, Explicit, Subclass}
|
|
auto match {
|
|
case Explicit => if(name.isEmpty) error("No name specified to load explicitly.") else Seq(new ToLoad(name))
|
|
case Subclass => discover(analysis, module, new inc.Discovery(Set(name), Set.empty))
|
|
case Annotation => discover(analysis, module, new inc.Discovery(Set.empty, Set(name)))
|
|
}
|
|
}
|
|
def discover(analysis: inc.Analysis, command: DiscoverCommand): Seq[ToLoad] =
|
|
discover(analysis, command.module, command.discovery)
|
|
|
|
def discover(analysis: inc.Analysis, module: Option[Boolean], discovery: inc.Discovery): Seq[ToLoad] =
|
|
{
|
|
for(src <- analysis.apis.internal.values.toSeq;
|
|
(df, found) <- discovery(src.definitions) if !found.isEmpty && moduleMatches(found.isModule, module))
|
|
yield
|
|
new ToLoad(df.name, found.isModule)
|
|
}
|
|
def moduleMatches(isModule: Boolean, expected: Option[Boolean]): Boolean =
|
|
expected.isEmpty || (Some(isModule) == expected)
|
|
|
|
def binaries(classpath: Seq[File], toLoad: Seq[ToLoad], parent: ClassLoader)(newImpl: Class[_] => Any): Seq[Any] =
|
|
loadBinaries(toLoad, toLoader(classpath, parent))(newImpl)
|
|
|
|
def loadBinaries(toLoad: Seq[ToLoad], loader: ClassLoader)(newImpl: Class[_] => Any): Seq[Any] =
|
|
for(ToLoad(name, module) <- toLoad if !name.isEmpty) yield
|
|
loadBinary(name, module, loader)(newImpl)
|
|
|
|
def loadBinary(name: String, module: Boolean, loader: ClassLoader)(newImpl: Class[_] => Any): Any =
|
|
if(module)
|
|
getObject(name, loader)
|
|
else
|
|
newImpl( Class.forName(name, true, loader) )
|
|
|
|
def check[T](discovered: Seq[T], allowMultiple: Boolean): Seq[T] =
|
|
discovered match
|
|
{
|
|
case Seq() => error("No project found")
|
|
case Seq(x) => discovered
|
|
case _ => if(allowMultiple) discovered else error("Multiple projects found: " + discovered.mkString(", "))
|
|
}
|
|
|
|
def error(msg: String) = throw new BuildException(msg)
|
|
} |