mirror of https://github.com/sbt/sbt.git
Fix compilation test, add scaladoc interface, remove structural types (#2265)
This commit is contained in:
parent
34fd967c66
commit
85346bd9d8
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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..*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue