mirror of https://github.com/sbt/sbt.git
implement embedded interface
This commit is contained in:
parent
d837f869bd
commit
2bd103f1fa
|
|
@ -12,7 +12,7 @@ package compiler
|
|||
* 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.*/
|
||||
class AnalyzingCompiler(val scalaInstance: ScalaInstance, val provider: CompilerInterfaceProvider, val cp: ClasspathOptions, log: Logger)
|
||||
class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, val provider: CompilerInterfaceProvider, val cp: xsbti.compile.ClasspathOptions, log: Logger)
|
||||
{
|
||||
def this(scalaInstance: ScalaInstance, provider: CompilerInterfaceProvider, log: Logger) = this(scalaInstance, provider, ClasspathOptions.auto, log)
|
||||
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
||||
|
|
@ -73,3 +73,34 @@ class AnalyzingCompiler(val scalaInstance: ScalaInstance, val provider: Compiler
|
|||
}
|
||||
override def toString = "Analyzing compiler (Scala " + scalaInstance.actualVersion + ")"
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ package compiler
|
|||
* order to add these jars to the boot classpath. The 'scala.home' property must be unset because Scala
|
||||
* puts jars in that directory on the bootclasspath. Because we use multiple Scala versions,
|
||||
* this would lead to compiling against the wrong library jar.*/
|
||||
final class CompilerArguments(scalaInstance: ScalaInstance, cp: ClasspathOptions)
|
||||
final class CompilerArguments(scalaInstance: xsbti.compile.ScalaInstance, cp: xsbti.compile.ClasspathOptions)
|
||||
{
|
||||
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String]): Seq[String] =
|
||||
{
|
||||
|
|
@ -28,7 +28,7 @@ final class CompilerArguments(scalaInstance: ScalaInstance, cp: ClasspathOptions
|
|||
options ++ outputOption ++ bootClasspathOption ++ classpathOption ++ abs(sources)
|
||||
}
|
||||
def finishClasspath(classpath: Seq[File]): Seq[File] =
|
||||
filterLibrary(classpath) ++ include(cp.compiler, scalaInstance.compilerJar) ++ include(cp.extra, scalaInstance.extraJars : _*)
|
||||
filterLibrary(classpath) ++ include(cp.compiler, scalaInstance.compilerJar) ++ include(cp.extra, scalaInstance.otherJars : _*)
|
||||
private def include(flag: Boolean, jars: File*) = if(flag) jars else Nil
|
||||
protected def abs(files: Seq[File]) = files.map(_.getAbsolutePath).sortWith(_ < _)
|
||||
protected def checkScalaHomeUnset()
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ package compiler
|
|||
|
||||
trait CompilerInterfaceProvider
|
||||
{
|
||||
def apply(scalaInstance: ScalaInstance, log: Logger): File
|
||||
def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File
|
||||
}
|
||||
object CompilerInterfaceProvider
|
||||
{
|
||||
def constant(file: File): CompilerInterfaceProvider = new CompilerInterfaceProvider {
|
||||
def apply(scalaInstance: ScalaInstance, log: Logger): File = file
|
||||
def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = file
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ package compiler
|
|||
* is used, for example, to compile the interface/plugin code.
|
||||
* If `explicitClasspath` is true, the bootclasspath and classpath are not augmented. If it is false,
|
||||
* the scala-library.jar from `scalaInstance` is put on bootclasspath and the scala-compiler jar goes on the classpath.*/
|
||||
class RawCompiler(val scalaInstance: ScalaInstance, cp: ClasspathOptions, log: Logger)
|
||||
class RawCompiler(val scalaInstance: xsbti.compile.ScalaInstance, cp: ClasspathOptions, log: Logger)
|
||||
{
|
||||
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ import inc._
|
|||
|
||||
final class CompileConfiguration(val sources: Seq[File], val classpath: Seq[File],
|
||||
val previousAnalysis: Analysis, val previousSetup: Option[CompileSetup], val currentSetup: CompileSetup, val getAnalysis: File => Option[Analysis], val definesClass: DefinesClass,
|
||||
val maxErrors: Int, val compiler: AnalyzingCompiler, val javac: JavaCompiler)
|
||||
val maxErrors: Int, val compiler: AnalyzingCompiler, val javac: xsbti.compile.JavaCompiler)
|
||||
|
||||
class AggressiveCompile(cacheFile: File)
|
||||
{
|
||||
def apply(compiler: AnalyzingCompiler, javac: JavaCompiler, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: File => Option[Analysis] = const(None), definesClass: DefinesClass = Locate.definesClass _, maxErrors: Int = 100, compileOrder: CompileOrder = Mixed, skip: Boolean = false)(implicit log: Logger): Analysis =
|
||||
def apply(compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String] = Nil, javacOptions: Seq[String] = Nil, analysisMap: File => Option[Analysis] = const(None), definesClass: DefinesClass = Locate.definesClass _, maxErrors: Int = 100, compileOrder: CompileOrder = Mixed, skip: Boolean = false)(implicit log: Logger): Analysis =
|
||||
{
|
||||
val setup = new CompileSetup(outputDirectory, new CompileOptions(options, javacOptions), compiler.scalaInstance.actualVersion, compileOrder)
|
||||
compile1(sources, classpath, setup, store, analysisMap, definesClass, compiler, javac, maxErrors, skip)
|
||||
|
|
@ -33,7 +33,7 @@ class AggressiveCompile(cacheFile: File)
|
|||
def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] =
|
||||
args.bootClasspath ++ args.finishClasspath(classpath)
|
||||
|
||||
def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: File => Option[Analysis], definesClass: DefinesClass, compiler: AnalyzingCompiler, javac: JavaCompiler, maxErrors: Int, skip: Boolean)(implicit log: Logger): Analysis =
|
||||
def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: File => Option[Analysis], definesClass: DefinesClass, compiler: AnalyzingCompiler, javac: xsbti.compile.JavaCompiler, maxErrors: Int, skip: Boolean)(implicit log: Logger): Analysis =
|
||||
{
|
||||
val (previousAnalysis, previousSetup) = extract(store.get())
|
||||
if(skip)
|
||||
|
|
@ -75,7 +75,7 @@ class AggressiveCompile(cacheFile: File)
|
|||
val loader = ClasspathUtilities.toLoader(searchClasspath)
|
||||
def readAPI(source: File, classes: Seq[Class[_]]) { callback.api(source, ClassToAPI(classes)) }
|
||||
Analyze(outputDirectory, javaSrcs, log)(callback, loader, readAPI) {
|
||||
javac(javaSrcs, absClasspath, outputDirectory, options.javacOptions)
|
||||
javac.compile(javaSrcs.toArray, absClasspath.toArray, outputDirectory, options.javacOptions.toArray, maxErrors, log)
|
||||
}
|
||||
}
|
||||
if(order == JavaThenScala) { compileJava(); compileScala() } else { compileScala(); compileJava() }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package sbt.compiler
|
||||
|
||||
import sbt.inc.Analysis
|
||||
import xsbti.{Logger, Maybe}
|
||||
import xsbti.compile._
|
||||
|
||||
import java.io.File
|
||||
|
||||
object IC extends IncrementalCompiler[Analysis, AnalyzingCompiler]
|
||||
{
|
||||
def compile(in: Inputs[Analysis, AnalyzingCompiler], log: Logger): Analysis =
|
||||
{
|
||||
val setup = in.setup; import setup._
|
||||
val options = in.options; import options.{options => scalacOptions, _}
|
||||
val compilers = in.compilers; import compilers._
|
||||
val agg = new AggressiveCompile(setup.cacheFile)
|
||||
val aMap = (f: File) => m2o(analysisMap(f))
|
||||
val defClass = (f: File) => { val dc = definesClass(f); (name: String) => dc.apply(name) }
|
||||
agg(scalac, javac, sources, classpath, classesDirectory, scalacOptions, javacOptions, aMap, defClass, maxErrors, order, skip)(log)
|
||||
}
|
||||
|
||||
private[this] def m2o[S](opt: Maybe[S]): Option[S] = if(opt.isEmpty) None else Some(opt.get)
|
||||
|
||||
def newScalaCompiler(instance: ScalaInstance, interfaceJar: File, options: ClasspathOptions, log: Logger): AnalyzingCompiler =
|
||||
new AnalyzingCompiler(instance, CompilerInterfaceProvider.constant(interfaceJar), options, log)
|
||||
|
||||
def compileInterfaceJar(label: String, sourceJar: File, targetJar: File, interfaceJar: File, instance: ScalaInstance, log: Logger)
|
||||
{
|
||||
val raw = new RawCompiler(instance, sbt.ClasspathOptions.auto, log)
|
||||
AnalyzingCompiler.compileSources(sourceJar :: Nil, targetJar, interfaceJar :: Nil, label, raw, log)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ object ComponentCompiler
|
|||
|
||||
def interfaceProvider(manager: ComponentManager): CompilerInterfaceProvider = new CompilerInterfaceProvider
|
||||
{
|
||||
def apply(scalaInstance: ScalaInstance, log: Logger): File =
|
||||
def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File =
|
||||
{
|
||||
// this is the instance used to compile the interface component
|
||||
val componentCompiler = new ComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager)
|
||||
|
|
@ -32,7 +32,6 @@ object ComponentCompiler
|
|||
class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager)
|
||||
{
|
||||
import ComponentCompiler._
|
||||
import sbt.IO.{copy, createDirectory, zip, jars, unzip, withTemporaryDirectory}
|
||||
def apply(id: String): File =
|
||||
try { getPrecompiled(id) }
|
||||
catch { case _: InvalidComponent => getLocallyCompiled(id) }
|
||||
|
|
@ -56,37 +55,11 @@ class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager)
|
|||
protected def compileAndInstall(id: String, binID: String)
|
||||
{
|
||||
val srcID = id + srcExtension
|
||||
withTemporaryDirectory { binaryDirectory =>
|
||||
IO.withTemporaryDirectory { binaryDirectory =>
|
||||
val targetJar = new File(binaryDirectory, id + ".jar")
|
||||
compileSources(manager.files(srcID)(IfMissing.Fail), targetJar, id)
|
||||
val xsbtiJars = manager.files(xsbtiID)(IfMissing.Fail)
|
||||
AnalyzingCompiler.compileSources(manager.files(srcID)(IfMissing.Fail), targetJar, xsbtiJars, id, compiler, manager.log)
|
||||
manager.define(binID, Seq(targetJar))
|
||||
}
|
||||
}
|
||||
/** 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.*/
|
||||
private def compileSources(sourceJars: Iterable[File], targetJar: File, id: String)
|
||||
{
|
||||
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 =>
|
||||
val xsbtiJars = manager.files(xsbtiID)(IfMissing.Fail)
|
||||
manager.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)
|
||||
manager.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")
|
||||
}
|
||||
|
|
@ -52,9 +52,10 @@ public interface IncrementalCompiler<Analysis, ScalaCompiler>
|
|||
* to create a ScalaCompiler for incremental compilation. It is the client's responsibility to manage compiled jars for
|
||||
* different Scala versions.
|
||||
*
|
||||
* @param label A brief name describing the source component for use in error messages
|
||||
* @param sourceJar The jar file containing the compiler interface sources. These are published as sbt's compiler-interface-src module.
|
||||
* @param targetJar Where to create the output jar file containing the compiled classes.
|
||||
* @param instance The ScalaInstance to compile the compiler interface for.
|
||||
* @param log The logger to use during compilation. */
|
||||
void compileInterfaceJar(File sourceJar, File targetJar, ScalaInstance instance, Logger log);
|
||||
void compileInterfaceJar(String label, File sourceJar, File targetJar, File interfaceJar, ScalaInstance instance, Logger log);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ public interface Setup<Analysis>
|
|||
/** If true, no sources are actually compiled and the Analysis from the previous compilation is returned.*/
|
||||
boolean skip();
|
||||
|
||||
/** The directory used to cache information across compilations.
|
||||
* This directory can be removed to force a full recompilation.
|
||||
* The directory should be unique and not shared between compilations. */
|
||||
File cacheDirectory();
|
||||
/** The file used to cache information across compilations.
|
||||
* This file can be removed to force a full recompilation.
|
||||
* The file should be unique and not shared between compilations. */
|
||||
File cacheFile();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -285,5 +285,5 @@ object BuildPaths
|
|||
final val GlobalPluginsProperty = "sbt.global.plugins"
|
||||
final val GlobalSettingsProperty = "sbt.global.settings"
|
||||
|
||||
def crossPath(base: File, instance: ScalaInstance): File = base / ("scala_" + instance.version)
|
||||
def crossPath(base: File, instance: xsbti.compile.ScalaInstance): File = base / ("scala_" + instance.version)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue