reorganize compilation modules

This commit is contained in:
Mark Harrah 2012-04-18 08:07:53 -04:00
parent db7ec09591
commit 8594e4443d
9 changed files with 106 additions and 73 deletions

View File

@ -12,9 +12,9 @@ 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 manager: ComponentManager, val cp: ClasspathOptions, log: Logger)
class AnalyzingCompiler(val scalaInstance: ScalaInstance, val provider: CompilerInterfaceProvider, val cp: ClasspathOptions, log: Logger)
{
def this(scalaInstance: ScalaInstance, manager: ComponentManager, log: Logger) = this(scalaInstance, manager, ClasspathOptions.auto, log)
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)
{
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options)
@ -48,7 +48,7 @@ class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: Component
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)
}
def force(log: Logger): Unit = getInterfaceJar(log)
def force(log: Logger): Unit = provider(scalaInstance, log)
private def call(interfaceClassName: String, log: Logger)(argTypes: Class[_]*)(args: AnyRef*)
{
val interfaceClass = getInterfaceClass(interfaceClassName, log)
@ -59,20 +59,12 @@ class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: Component
}
private[this] def loader =
{
val interfaceJar = getInterfaceJar(log)
val interfaceJar = provider(scalaInstance, log)
// this goes to scalaInstance.loader for scala classes and the loader of this class for xsbti classes
val dual = createDualLoader(scalaInstance.loader, getClass.getClassLoader)
new URLClassLoader(Array(interfaceJar.toURI.toURL), dual)
}
private def getInterfaceClass(name: String, log: Logger) = Class.forName(name, true, loader)
private def getInterfaceJar(log: Logger) =
{
// this is the instance used to compile the interface component
val componentCompiler = newComponentCompiler(log)
log.debug("Getting " + ComponentCompiler.compilerInterfaceID + " from component compiler for Scala " + scalaInstance.version)
componentCompiler(ComponentCompiler.compilerInterfaceID)
}
def newComponentCompiler(log: Logger) = new ComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager)
protected def createDualLoader(scalaLoader: ClassLoader, sbtLoader: ClassLoader): ClassLoader =
{
val xsbtiFilter = (name: String) => name.startsWith("xsbti.")
@ -80,4 +72,4 @@ class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: Component
new classpath.DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false)
}
override def toString = "Analyzing compiler (Scala " + scalaInstance.actualVersion + ")"
}
}

View File

@ -4,6 +4,7 @@
package sbt
package compiler
import xsbti.ArtifactInfo
import scala.util
import java.io.File
import CompilerArguments.{abs, absString, BootClasspathOption}
@ -48,7 +49,7 @@ final class CompilerArguments(scalaInstance: ScalaInstance, cp: ClasspathOptions
originalBoot
}
def filterLibrary(classpath: Seq[File]) =
if(cp.filterLibrary) classpath.filterNot(_.getName contains ScalaArtifacts.LibraryID) else classpath
if(cp.filterLibrary) classpath.filterNot(_.getName contains ArtifactInfo.ScalaLibraryID) else classpath
def bootClasspathOption = if(cp.autoBoot) Seq(BootClasspathOption, createBootClasspath) else Nil
def bootClasspath = if(cp.autoBoot) IO.parseClasspath(createBootClasspath) else Nil
}

View File

@ -0,0 +1,15 @@
package sbt
package compiler
import java.io.File
trait CompilerInterfaceProvider
{
def apply(scalaInstance: ScalaInstance, log: Logger): File
}
object CompilerInterfaceProvider
{
def constant(file: File): CompilerInterfaceProvider = new CompilerInterfaceProvider {
def apply(scalaInstance: ScalaInstance, log: Logger): File = file
}
}

View File

@ -2,11 +2,11 @@
* Copyright 2010 Mark Harrah
*/
package sbt
package compiler
import inc._
import java.io.File
import compiler.{AnalyzingCompiler, CompilerArguments, JavaCompiler}
import classpath.ClasspathUtilities
import classfile.Analyze
import xsbti.api.Source
@ -108,7 +108,7 @@ class AggressiveCompile(cacheFile: File)
import AnalysisFormats._
val store = AggressiveCompile.staticCache(cacheFile, AnalysisStore.sync(AnalysisStore.cached(FileBasedStore(cacheFile))))
}
private object AggressiveCompile
object AggressiveCompile
{
import collection.mutable
import java.lang.ref.{Reference,SoftReference}
@ -121,4 +121,53 @@ private object AggressiveCompile
b
}
}
}
def directOrFork(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File]): JavaCompiler =
if(javaHome.isDefined)
JavaCompiler.fork(cpOptions, instance)(forkJavac(javaHome))
else
JavaCompiler.directOrFork(cpOptions, instance)(forkJavac(None))
def forkJavac(javaHome: Option[File]): JavaCompiler.Fork =
{
import Path._
def exec(jc: JavacContract) = javaHome match { case None => jc.name; case Some(jh) => (jh / "bin" / jc.name).absolutePath }
(contract: JavacContract, args: Seq[String], log: Logger) => {
log.debug("Forking " + contract.name + ": " + exec(contract) + " " + args.mkString(" "))
val javacLogger = new JavacLogger(log)
var exitCode = -1
try {
exitCode = Process(exec(contract), args) ! javacLogger
} finally {
javacLogger.flush(exitCode)
}
exitCode
}
}
}
private[sbt] class JavacLogger(log: Logger) extends ProcessLogger {
import scala.collection.mutable.ListBuffer
import Level.{Info, Warn, Error, Value => LogLevel}
private val msgs: ListBuffer[(LogLevel, String)] = new ListBuffer()
def info(s: => String): Unit =
synchronized { msgs += ((Info, s)) }
def error(s: => String): Unit =
synchronized { msgs += ((Error, s)) }
def buffer[T](f: => T): T = f
private def print(desiredLevel: LogLevel)(t: (LogLevel, String)) = t match {
case (Info, msg) => log.info(msg)
case (Error, msg) => log.log(desiredLevel, msg)
}
def flush(exitCode: Int): Unit = {
val level = if (exitCode == 0) Warn else Error
msgs foreach print(level)
msgs.clear()
}
}

View File

@ -14,6 +14,17 @@ object ComponentCompiler
val compilerInterfaceID = "compiler-interface"
val compilerInterfaceSrcID = compilerInterfaceID + srcExtension
val javaVersion = System.getProperty("java.class.version")
def interfaceProvider(manager: ComponentManager): CompilerInterfaceProvider = new CompilerInterfaceProvider
{
def apply(scalaInstance: 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)
log.debug("Getting " + compilerInterfaceID + " from component compiler for Scala " + scalaInstance.version)
componentCompiler(compilerInterfaceID)
}
}
}
/** This class provides source components compiled with the provided RawCompiler.
* The compiled classes are cached using the provided component manager according

View File

@ -0,0 +1,9 @@
package xsbti;
public final class ArtifactInfo
{
public static final String ScalaOrganization = "org.scala-lang";
public static final String ScalaLibraryID = "scala-library";
public static final String ScalaCompilerID = "scala-compiler";
public static final String SbtOrganization = "org.scala-sbt";
}

View File

@ -14,14 +14,16 @@ import plugins.matcher.ExactPatternMatcher
object ScalaArtifacts
{
val Organization = "org.scala-lang"
val LibraryID = "scala-library"
val CompilerID = "scala-compiler"
import xsbti.ArtifactInfo._
val Organization = ScalaOrganization
val LibraryID = ScalaLibraryID
val CompilerID = ScalaCompilerID
def libraryDependency(version: String): ModuleID = ModuleID(Organization, LibraryID, version)
}
object SbtArtifacts
{
val Organization = "org.scala-sbt"
import xsbti.ArtifactInfo._
val Organization = SbtOrganization
}
import ScalaArtifacts._

View File

@ -46,7 +46,7 @@ object Compiler
def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File])(implicit app: AppConfiguration, log: Logger): Compilers =
{
val javac = directOrFork(instance, cpOptions, javaHome)
val javac = AggressiveCompile.directOrFork(instance, cpOptions, javaHome)
compilers(instance, cpOptions, javac)
}
def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javac: JavaCompiler.Fork)(implicit app: AppConfiguration, log: Logger): Compilers =
@ -63,29 +63,8 @@ object Compiler
{
val launcher = app.provider.scalaProvider.launcher
val componentManager = new ComponentManager(launcher.globalLock, app.provider.components, Option(launcher.ivyHome), log)
new AnalyzingCompiler(instance, componentManager, cpOptions, log)
}
def directOrFork(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File]): JavaCompiler =
if(javaHome.isDefined)
JavaCompiler.fork(cpOptions, instance)(forkJavac(javaHome))
else
JavaCompiler.directOrFork(cpOptions, instance)(forkJavac(None))
def forkJavac(javaHome: Option[File]): JavaCompiler.Fork =
{
import Path._
def exec(jc: JavacContract) = javaHome match { case None => jc.name; case Some(jh) => (jh / "bin" / jc.name).absolutePath }
(contract: JavacContract, args: Seq[String], log: Logger) => {
log.debug("Forking " + contract.name + ": " + exec(contract) + " " + args.mkString(" "))
val javacLogger = new JavacLogger(log)
var exitCode = -1
try {
exitCode = Process(exec(contract), args) ! javacLogger
} finally {
javacLogger.flush(exitCode)
}
exitCode
}
val provider = ComponentCompiler.interfaceProvider(componentManager)
new AnalyzingCompiler(instance, provider, cpOptions, log)
}
def apply(in: Inputs, log: Logger): Analysis =
@ -98,29 +77,3 @@ object Compiler
agg(scalac, javac, sources, classpath, classesDirectory, options, javacOptions, analysisMap, definesClass, maxErrors, order, skip)(log)
}
}
private[sbt] class JavacLogger(log: Logger) extends ProcessLogger {
import scala.collection.mutable.ListBuffer
import Level.{Info, Warn, Error, Value => LogLevel}
private val msgs: ListBuffer[(LogLevel, String)] = new ListBuffer()
def info(s: => String): Unit =
synchronized { msgs += ((Info, s)) }
def error(s: => String): Unit =
synchronized { msgs += ((Error, s)) }
def buffer[T](f: => T): T = f
private def print(desiredLevel: LogLevel)(t: (LogLevel, String)) = t match {
case (Info, msg) => log.info(msg)
case (Error, msg) => log.log(desiredLevel, msg)
}
def flush(exitCode: Int): Unit = {
val level = if (exitCode == 0) Warn else Error
msgs foreach print(level)
msgs.clear()
}
}

View File

@ -102,10 +102,11 @@ object Sbt extends Build
// Persists the incremental data structures using SBinary
lazy val compilePersistSub = baseProject(compilePath / "persist", "Persist") dependsOn(compileIncrementalSub, apiSub) settings(sbinary)
// sbt-side interface to compiler. Calls compiler-side interface reflectively
lazy val compilerSub = testedBaseProject(compilePath, "Compile") dependsOn(launchInterfaceSub, interfaceSub % "compile;test->test", ivySub, ioSub, classpathSub,
lazy val compilerSub = testedBaseProject(compilePath, "Compile") dependsOn(launchInterfaceSub, interfaceSub % "compile;test->test", logSub, ioSub, classpathSub,
logSub % "test->test", launchSub % "test->test", apiSub % "test") settings( compilerSettings : _*)
lazy val compilerIntegrationSub = baseProject(compilePath / "integration", "Compiler Integration") dependsOn(
compileIncrementalSub, compilerSub, compilePersistSub, apiSub, classfileSub)
lazy val compilerIvySub = baseProject(compilePath / "ivy", "Compiler Ivy Integration") dependsOn(ivySub, compilerSub )
lazy val scriptedBaseSub = baseProject(scriptedPath / "base", "Scripted Framework") dependsOn(ioSub, processSub)
lazy val scriptedSbtSub = baseProject(scriptedPath / "sbt", "Scripted sbt") dependsOn(ioSub, logSub, processSub, scriptedBaseSub, launchInterfaceSub % "provided")
@ -114,7 +115,7 @@ object Sbt extends Build
// Implementation and support code for defining actions.
lazy val actionsSub = baseProject(mainPath / "actions", "Actions") dependsOn(
classpathSub, completeSub, apiSub, compilerIntegrationSub,
classpathSub, completeSub, apiSub, compilerIntegrationSub, compilerIvySub,
interfaceSub, ioSub, ivySub, logSub, processSub, runSub, stdTaskSub, taskSub, trackingSub, testingSub)
lazy val commandSub = testedBaseProject(commandPath, "Command") dependsOn(interfaceSub, ioSub, launchInterfaceSub, logSub, completeSub, classpathSub)