2010-07-05 18:53:37 +02:00
|
|
|
/* sbt -- Simple Build Tool
|
|
|
|
|
* Copyright 2009, 2010 Mark Harrah
|
|
|
|
|
*/
|
|
|
|
|
package sbt
|
2011-03-02 12:46:28 +01:00
|
|
|
package compiler
|
2009-09-05 21:01:04 +02:00
|
|
|
|
2010-10-23 22:34:22 +02:00
|
|
|
import xsbti.{AnalysisCallback, Logger => xLogger, Reporter}
|
2009-09-05 21:01:04 +02:00
|
|
|
import java.io.File
|
2009-10-08 03:27:53 +02:00
|
|
|
import java.net.{URL, URLClassLoader}
|
2009-09-05 21:01:04 +02:00
|
|
|
|
|
|
|
|
/** Interface to the Scala compiler that uses the dependency analysis plugin. This class uses the Scala library and compiler
|
2009-09-06 22:05:31 +02:00
|
|
|
* provided by scalaInstance. This class requires a ComponentManager in order to obtain the interface code to scalac and
|
|
|
|
|
* the analysis plugin. Because these call Scala code for a different Scala version than the one used for this class, they must
|
|
|
|
|
* be compiled for the version of Scala being used.*/
|
2012-04-18 22:01:45 +02:00
|
|
|
class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, val provider: CompilerInterfaceProvider, val cp: xsbti.compile.ClasspathOptions, log: Logger)
|
2009-09-05 21:01:04 +02:00
|
|
|
{
|
2012-04-18 14:07:53 +02:00
|
|
|
def this(scalaInstance: ScalaInstance, provider: CompilerInterfaceProvider, log: Logger) = this(scalaInstance, provider, ClasspathOptions.auto, log)
|
2010-07-05 18:53:37 +02:00
|
|
|
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
2009-09-05 21:01:04 +02:00
|
|
|
{
|
2010-05-05 14:30:03 +02:00
|
|
|
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options)
|
2010-06-27 15:16:53 +02:00
|
|
|
compile(arguments, callback, maximumErrors, log)
|
|
|
|
|
}
|
2010-10-23 22:34:22 +02:00
|
|
|
|
|
|
|
|
def compile(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger): Unit =
|
|
|
|
|
compile(arguments, callback, log, new LoggerReporter(maximumErrors, log))
|
|
|
|
|
def compile(arguments: Seq[String], callback: AnalysisCallback, log: Logger, reporter: Reporter)
|
2010-06-27 15:16:53 +02:00
|
|
|
{
|
2009-10-08 03:27:53 +02:00
|
|
|
call("xsbt.CompilerInterface", log)(
|
2010-10-23 22:34:22 +02:00
|
|
|
classOf[Array[String]], classOf[AnalysisCallback], classOf[xLogger], classOf[Reporter] ) (
|
|
|
|
|
arguments.toArray[String] : Array[String], callback, log, reporter )
|
2009-10-03 15:39:16 +02:00
|
|
|
}
|
2010-10-23 22:34:22 +02:00
|
|
|
|
2010-07-05 18:53:37 +02:00
|
|
|
def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: Logger): Unit =
|
2010-10-23 22:34:22 +02:00
|
|
|
doc(sources, classpath, outputDirectory, options, log, new LoggerReporter(maximumErrors, log))
|
|
|
|
|
def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger, reporter: Reporter): Unit =
|
2009-10-03 15:39:16 +02:00
|
|
|
{
|
2010-05-05 14:30:03 +02:00
|
|
|
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options)
|
2010-10-23 22:34:22 +02:00
|
|
|
call("xsbt.ScaladocInterface", log) (classOf[Array[String]], classOf[xLogger], classOf[Reporter]) (
|
|
|
|
|
arguments.toArray[String] : Array[String], log, reporter)
|
2009-10-08 03:27:53 +02:00
|
|
|
}
|
2011-10-16 23:27:36 +02:00
|
|
|
def console(classpath: Seq[File], options: Seq[String], initialCommands: String, cleanupCommands: String, log: Logger)(loader: Option[ClassLoader] = None, bindings: Seq[(String, Any)] = Nil): Unit =
|
2009-10-08 03:27:53 +02:00
|
|
|
{
|
2010-05-05 14:30:03 +02:00
|
|
|
val arguments = new CompilerArguments(scalaInstance, cp)
|
2011-03-23 12:06:51 +01:00
|
|
|
val classpathString = CompilerArguments.absString(arguments.finishClasspath(classpath))
|
2010-05-05 14:30:03 +02:00
|
|
|
val bootClasspath = if(cp.autoBoot) arguments.createBootClasspath else ""
|
2010-09-04 14:16:22 +02:00
|
|
|
val (names, values) = bindings.unzip
|
|
|
|
|
call("xsbt.ConsoleInterface", log)(
|
2011-10-16 23:27:36 +02:00
|
|
|
classOf[Array[String]], classOf[String], classOf[String], classOf[String], classOf[String], classOf[ClassLoader], classOf[Array[String]], classOf[Array[Any]], classOf[xLogger])(
|
|
|
|
|
options.toArray[String]: Array[String], bootClasspath, classpathString, initialCommands, cleanupCommands, loader.orNull, names.toArray[String], values.toArray[Any], log)
|
2009-10-08 03:27:53 +02:00
|
|
|
}
|
2012-04-18 14:07:53 +02:00
|
|
|
def force(log: Logger): Unit = provider(scalaInstance, log)
|
2010-07-05 18:53:37 +02:00
|
|
|
private def call(interfaceClassName: String, log: Logger)(argTypes: Class[_]*)(args: AnyRef*)
|
2009-10-08 03:27:53 +02:00
|
|
|
{
|
|
|
|
|
val interfaceClass = getInterfaceClass(interfaceClassName, log)
|
2009-10-03 15:39:16 +02:00
|
|
|
val interface = interfaceClass.newInstance.asInstanceOf[AnyRef]
|
2009-10-08 03:27:53 +02:00
|
|
|
val method = interfaceClass.getMethod("run", argTypes : _*)
|
2009-10-10 01:12:14 +02:00
|
|
|
try { method.invoke(interface, args: _*) }
|
|
|
|
|
catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
|
2009-10-03 15:39:16 +02:00
|
|
|
}
|
2010-03-13 19:25:08 +01:00
|
|
|
private[this] def loader =
|
2009-10-03 15:39:16 +02:00
|
|
|
{
|
2012-04-18 14:07:53 +02:00
|
|
|
val interfaceJar = provider(scalaInstance, log)
|
2010-10-23 03:55:16 +02:00
|
|
|
// this goes to scalaInstance.loader for scala classes and the loader of this class for xsbti classes
|
|
|
|
|
val dual = createDualLoader(scalaInstance.loader, getClass.getClassLoader)
|
2010-03-13 19:25:08 +01:00
|
|
|
new URLClassLoader(Array(interfaceJar.toURI.toURL), dual)
|
2009-09-05 21:01:04 +02:00
|
|
|
}
|
2010-07-05 18:53:37 +02:00
|
|
|
private def getInterfaceClass(name: String, log: Logger) = Class.forName(name, true, loader)
|
2009-09-05 21:01:04 +02:00
|
|
|
protected def createDualLoader(scalaLoader: ClassLoader, sbtLoader: ClassLoader): ClassLoader =
|
|
|
|
|
{
|
|
|
|
|
val xsbtiFilter = (name: String) => name.startsWith("xsbti.")
|
|
|
|
|
val notXsbtiFilter = (name: String) => !xsbtiFilter(name)
|
2010-07-05 18:53:37 +02:00
|
|
|
new classpath.DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false)
|
2009-09-05 21:01:04 +02:00
|
|
|
}
|
|
|
|
|
override def toString = "Analyzing compiler (Scala " + scalaInstance.actualVersion + ")"
|
2012-04-18 14:07:53 +02:00
|
|
|
}
|
2012-04-18 22:01:45 +02:00
|
|
|
object AnalyzingCompiler
|
|
|
|
|
{
|
|
|
|
|
import sbt.IO.{copy, createDirectory, zip, jars, unzip, withTemporaryDirectory}
|
|
|
|
|
|
|
|
|
|
/** Extract sources from source jars, compile them with the xsbti interfaces on the classpath, and package the compiled classes and
|
|
|
|
|
* any resources from the source jars into a final jar.*/
|
|
|
|
|
def compileSources(sourceJars: Iterable[File], targetJar: File, xsbtiJars: Iterable[File], id: String, compiler: RawCompiler, log: Logger)
|
|
|
|
|
{
|
|
|
|
|
val isSource = (f: File) => isSourceName(f.getName)
|
|
|
|
|
def keepIfSource(files: Set[File]): Set[File] = if(files.exists(isSource)) files else Set()
|
|
|
|
|
|
|
|
|
|
withTemporaryDirectory { dir =>
|
|
|
|
|
val extractedSources = (Set[File]() /: sourceJars) { (extracted, sourceJar)=> extracted ++ keepIfSource(unzip(sourceJar, dir)) }
|
|
|
|
|
val (sourceFiles, resources) = extractedSources.partition(isSource)
|
|
|
|
|
withTemporaryDirectory { outputDirectory =>
|
|
|
|
|
log.info("'" + id + "' not yet compiled for Scala " + compiler.scalaInstance.actualVersion + ". Compiling...")
|
|
|
|
|
val start = System.currentTimeMillis
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
compiler(sourceFiles.toSeq, xsbtiJars.toSeq ++ sourceJars, outputDirectory, "-nowarn" :: Nil)
|
|
|
|
|
log.info(" Compilation completed in " + (System.currentTimeMillis - start) / 1000.0 + " s")
|
|
|
|
|
}
|
|
|
|
|
catch { case e: xsbti.CompileFailed => throw new CompileFailed(e.arguments, "Error compiling sbt component '" + id + "'") }
|
|
|
|
|
import sbt.Path._
|
|
|
|
|
copy(resources x rebase(dir, outputDirectory))
|
|
|
|
|
zip((outputDirectory ***) x_! relativeTo(outputDirectory), targetJar)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private def isSourceName(name: String): Boolean = name.endsWith(".scala") || name.endsWith(".java")
|
|
|
|
|
}
|