Fix compilation test, add scaladoc interface, remove structural types (#2265)

This commit is contained in:
Mark Harrah 2009-10-03 09:39:16 -04:00
parent 34fd967c66
commit 85346bd9d8
8 changed files with 102 additions and 33 deletions

View File

@ -16,18 +16,39 @@ class AnalyzingCompiler(scalaInstance: ScalaInstance, manager: ComponentManager)
callback: AnalysisCallback, maximumErrors: Int, log: CompileLogger)
{
val arguments = (new CompilerArguments(scalaInstance))(sources, classpath, outputDirectory, options, compilerOnClasspath)
// this is the instance used to compile the analysis
val componentCompiler = new ComponentCompiler(new RawCompiler(scalaInstance, log), manager)
val interfaceClass = getInterfaceClass("xsbt.CompilerInterface", log)
val interface = interfaceClass.newInstance.asInstanceOf[AnyRef]
// this is commented out because of Scala ticket #2365
/*val runnable = interface.asInstanceOf[{ def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: xLogger): Unit }]
// these arguments are safe to pass across the ClassLoader boundary because the types are defined in Java
// so they will be binary compatible across all versions of Scala
runnable.run(arguments.toArray, callback, maximumErrors, log)*/
val method = interfaceClass.getMethod("run", classOf[Array[String]], classOf[AnalysisCallback], classOf[Int], classOf[xLogger])
method.invoke(interface, arguments.toArray[String] : Array[String], callback, maximumErrors: java.lang.Integer, log)
}
def doc(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: CompileLogger): Unit =
doc(sources, classpath, outputDirectory, options, false, maximumErrors, log)
def doc(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], compilerOnClasspath: Boolean,
maximumErrors: Int, log: CompileLogger): Unit =
{
val arguments = (new CompilerArguments(scalaInstance))(sources, classpath, outputDirectory, options, compilerOnClasspath)
val interfaceClass = getInterfaceClass("xsbt.ScaladocInterface", log)
val interface = interfaceClass.newInstance.asInstanceOf[AnyRef]
val method = interfaceClass.getMethod("run", classOf[Array[String]], classOf[Int], classOf[xLogger])
method.invoke(interface, arguments.toArray[String] : Array[String], maximumErrors: java.lang.Integer, log)
}
private def getInterfaceClass(name: String, log: CompileLogger) =
{
// 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)
val interfaceJar = componentCompiler(ComponentCompiler.compilerInterfaceID)
val dual = createDualLoader(scalaInstance.loader, getClass.getClassLoader) // this goes to scalaLoader for scala classes and sbtLoader for xsbti classes
val interfaceLoader = new URLClassLoader(Array(interfaceJar.toURI.toURL), dual)
val interface = Class.forName("xsbt.CompilerInterface", true, interfaceLoader).newInstance.asInstanceOf[AnyRef]
val runnable = interface.asInstanceOf[{ def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: xLogger): Unit }]
// these arguments are safe to pass across the ClassLoader boundary because the types are defined in Java
// so they will be binary compatible across all versions of Scala
runnable.run(arguments.toArray, callback, maximumErrors, log)
Class.forName(name, true, interfaceLoader)
}
def newComponentCompiler(log: CompileLogger) = new ComponentCompiler(new RawCompiler(scalaInstance, log), manager)
protected def createDualLoader(scalaLoader: ClassLoader, sbtLoader: ClassLoader): ClassLoader =
{
val xsbtiFilter = (name: String) => name.startsWith("xsbti.")

View File

@ -23,6 +23,7 @@ class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager)
try { manager.file(binID) }
catch { case e: InvalidComponent => compileAndInstall(id, binID) }
}
def clearCache(id: String): Unit = manager.clearCache(binaryID(id))
protected def binaryID(id: String) = id + binSeparator + compiler.scalaInstance.actualVersion
protected def compileAndInstall(id: String, binID: String): File =
{
@ -45,7 +46,8 @@ class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager)
val (sourceFiles, resources) = extractedSources.partition(_.getName.endsWith(".scala"))
withTemporaryDirectory { outputDirectory =>
val xsbtiJars = manager.files(xsbtiID)
compiler(Set() ++ sourceFiles, Set() ++ xsbtiJars, outputDirectory, Nil, true)
try { compiler(Set() ++ sourceFiles, Set() ++ xsbtiJars, outputDirectory, Nil, true) }
catch { case e: xsbti.CompileFailed => throw new CompileFailed(e.arguments, "Error compiling component '" + id + "'") }
copy(resources x (FileMapper.rebase(dir, outputDirectory)))
zip((outputDirectory ***) x (PathMapper.relativeTo(outputDirectory)), targetJar)
}

View File

@ -14,10 +14,9 @@ class RawCompiler(val scalaInstance: ScalaInstance, log: CompileLogger)
// The following imports ensure there is a compile error if the identifiers change,
// but should not be otherwise directly referenced
import scala.tools.nsc.Main
import scala.tools.nsc.Properties
val arguments = (new CompilerArguments(scalaInstance))(sources, classpath, outputDirectory, options, compilerOnClasspath)
log.debug("Vanilla interface to Scala compiler " + scalaInstance.actualVersion + " with arguments: " + arguments.mkString("\n\t", "\n\t", ""))
log.debug("Plain interface to Scala compiler " + scalaInstance.actualVersion + " with arguments: " + arguments.mkString("\n\t", "\n\t", ""))
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaInstance.loader)
val process = mainClass.getMethod("process", classOf[Array[String]])
process.invoke(null, toJavaArray(arguments))
@ -26,8 +25,10 @@ class RawCompiler(val scalaInstance: ScalaInstance, log: CompileLogger)
protected def checkForFailure(mainClass: Class[_], args: Array[String])
{
val reporter = mainClass.getMethod("reporter").invoke(null)
val failed = reporter.asInstanceOf[{ def hasErrors: Boolean }].hasErrors
if(failed) throw new xsbti.CompileFailed { val arguments = args; override def toString = "Vanilla compile failed" }
// this is commented out because of Scala ticket #2365
//val failed = reporter.asInstanceOf[{ def hasErrors: Boolean }].hasErrors
val failed = reporter.getClass.getMethod("hasErrors").invoke(reporter).asInstanceOf[Boolean]
if(failed) throw new CompileFailed(args, "Plain compile failed")
}
protected def toJavaArray(arguments: Seq[String]): Array[String] =
{
@ -35,4 +36,5 @@ class RawCompiler(val scalaInstance: ScalaInstance, log: CompileLogger)
assert(realArray.getClass eq classOf[Array[String]])
realArray
}
}
}
class CompileFailed(val arguments: Array[String], override val toString: String) extends xsbti.CompileFailed

View File

@ -12,7 +12,7 @@ private final class LoggerReporter(maximumErrors: Int, log: Logger) extends scal
{
import scala.tools.nsc.util.{FakePos,NoPosition,Position}
private val positions = new scala.collection.mutable.HashMap[Position, Severity]
def error(msg: String) { error(FakePos("scalac"), msg) }
def printSummary()
@ -22,7 +22,7 @@ private final class LoggerReporter(maximumErrors: Int, log: Logger) extends scal
if(ERROR.count > 0)
log.error(Message(countElementsAsString(ERROR.count, "error") + " found"))
}
def display(pos: Position, msg: String, severity: Severity)
{
severity.count += 1
@ -39,7 +39,7 @@ private final class LoggerReporter(maximumErrors: Int, log: Logger) extends scal
case INFO => log.info(m)
})
}
private def print(logger: F0[String] => Unit, posIn: Position, msg: String)
{
def log(s: => String) = logger(Message(s))
@ -69,7 +69,7 @@ private final class LoggerReporter(maximumErrors: Int, log: Logger) extends scal
for(offset <- pos.offset; src <- pos.source)
{
val pointer = offset - src.lineToOffset(src.offsetToLine(offset))
val pointerSpace = lineContent.take(pointer).map { case '\t' => '\t'; case x => ' ' }
val pointerSpace = (lineContent: Seq[Char]).take(pointer).map { case '\t' => '\t'; case x => ' ' }
log(pointerSpace.mkString + "^") // pointer to the column position of the error/warning
}
}
@ -93,7 +93,7 @@ private final class LoggerReporter(maximumErrors: Int, log: Logger) extends scal
case _ => display(pos, msg, severity)
}
}
private def testAndLog(pos: Position, severity: Severity): Boolean =
{
if(pos == null || pos.offset.isEmpty)

View File

@ -11,8 +11,7 @@ class CompilerInterface
def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
{
def debug(msg: => String) = log.debug(Message(msg))
import scala.tools.nsc.{CompilerCommand, FatalError, Global, Settings, reporters, util}
import util.FakePos
import scala.tools.nsc.{CompilerCommand, Global, Settings}
debug("Interfacing (CompilerInterface) with Scala compiler " + scala.tools.nsc.Properties.versionString)
@ -37,13 +36,12 @@ class CompilerInterface
def newPhase(prev: Phase) = analyzer.newPhase(prev)
def name = phaseName
}
lazy val pdescriptors = // done this way for compatibility between 2.7 and 2.8
override def computePhaseDescriptors = // done this way for compatibility between 2.7 and 2.8
{
phasesSet += sbtAnalyzer
val superd = super.phaseDescriptors
if(superd.contains(sbtAnalyzer)) superd else ( super.phaseDescriptors ++ Seq(sbtAnalyzer) ).toList
val superd = super.computePhaseDescriptors
if(superd.contains(sbtAnalyzer)) superd else ( superd ++ Seq(sbtAnalyzer) ).toList
}
override def phaseDescriptors = pdescriptors
trait Compat27 { val runsBefore: List[String] = Nil }
}
if(!reporter.hasErrors)
@ -56,7 +54,35 @@ class CompilerInterface
if(reporter.hasErrors)
{
debug("Compilation failed (CompilerInterface)")
throw new xsbti.CompileFailed { val arguments = args; override def toString = "Analyzed compilation failed" }
throw new InterfaceCompileFailed(args, "Analyzed compilation failed")
}
}
}
}
class ScaladocInterface
{
def run(args: Array[String], maximumErrors: Int, log: Logger)
{
import scala.tools.nsc.{doc, CompilerCommand, Global}
val reporter = new LoggerReporter(maximumErrors, log)
val docSettings: doc.Settings = new doc.Settings(reporter.error)
val command = new CompilerCommand(args.toList, docSettings, error, false)
object compiler extends Global(command.settings, reporter)
{
override val onlyPresentation = true
}
if(!reporter.hasErrors)
{
val run = new compiler.Run
run compile command.files
val generator = new doc.DefaultDocDriver
{
lazy val global: compiler.type = compiler
lazy val settings = docSettings
}
generator.process(run.units)
}
reporter.printSummary()
if(reporter.hasErrors) throw new InterfaceCompileFailed(args, "Scaladoc generation failed")
}
}
class InterfaceCompileFailed(val arguments: Array[String], override val toString: String) extends xsbti.CompileFailed

View File

@ -17,6 +17,19 @@ object CheckBasic extends Specification
}
}
}
"Scaladoc on basic file should succeed" in {
WithFiles(basicName -> basicSource){ files =>
for(scalaVersion <- TestCompile.allVersions)
{
FileUtilities.withTemporaryDirectory { outputDirectory =>
WithCompiler(scalaVersion) { (compiler, log) =>
compiler.doc(Set() ++ files, Set.empty, outputDirectory, Nil, 5, log)
}
}
true must beTrue // don't know how to just check that previous line completes without exception
}
}
}
"Analyzer plugin should send source begin and end" in {
WithFiles(basicName -> basicSource) { files =>
for(scalaVersion <- TestCompile.allVersions)

View File

@ -37,20 +37,26 @@ object WithCompiler
boot.LaunchTest.withLauncher { launch =>
FileUtilities.withTemporaryDirectory { componentDirectory =>
val manager = new ComponentManager(new boot.ComponentProvider(componentDirectory), log)
val compiler = new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager)
compiler.newComponentCompiler(log).clearCache(ComponentCompiler.compilerInterfaceID)
prepare(manager, ComponentCompiler.compilerInterfaceSrcID, "CompilerInterface.scala")
prepare(manager, ComponentCompiler.xsbtiID, classOf[xsbti.AnalysisCallback])
f(new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager), log)
f(compiler, log)
}
}
}
}
private def prepare(manager: ComponentManager, id: String, resource: Class[_]): Unit =
manager.define(id, FileUtilities.classLocationFile(resource) :: Nil)
private def prepare(manager: ComponentManager, id: String, resource: String): Unit =
def prepare(manager: ComponentManager, id: String, resource: Class[_]): Unit = define(manager, id, FileUtilities.classLocationFile(resource) :: Nil)
def prepare(manager: ComponentManager, id: String, resource: String)
{
val src = getClass.getClassLoader.getResource(resource)
if(src eq null)
error("Resource not found: " + resource)
manager.define(id, FileUtilities.asFile(src) :: Nil)
define(manager, id, FileUtilities.asFile(src) :: Nil)
}
def define(manager: ComponentManager, id: String, files: List[File])
{
manager.clearCache(id)
manager.define(id, files)
}
}

View File

@ -14,11 +14,10 @@ object TestCompile
def apply[T](scalaVersion: String, sources: Set[File], outputDirectory: File, options: Seq[String], superclassNames: Seq[String])(f: (TestCallback, CompileLogger) => T): T =
{
val testCallback = new TestCallback(superclassNames.toArray)
val result = WithCompiler(scalaVersion) { (compiler, log) =>
WithCompiler(scalaVersion) { (compiler, log) =>
compiler(sources, Set.empty, outputDirectory, options, testCallback, 5, log)
f(testCallback, log)
}
result
}
/** Tests running the compiler interface with the analyzer plugin. The provided function is given a ClassLoader that can
* load the compiled classes..*/