mirror of https://github.com/sbt/sbt.git
fix tests, discovery
updated compile tests for new minimal AnalysisCallback moved discovery to discovery/ subproject and updated for new approach fixed discovery to only find public methods when searching for annotated definitions extracting inherited definitions unimplemented in api/, so some discovery tests fail moved discovery classes from sbt.inc package to sbt.compile
This commit is contained in:
parent
b6ff9d8661
commit
a3f1b9c22f
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
package inc
|
||||
package compile
|
||||
|
||||
final case class Discovered(baseClasses: Set[String], annotations: Set[String], hasMain: Boolean, isModule: Boolean)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
package inc
|
||||
package compile
|
||||
|
||||
import xsbti.api.{Path => APath, _}
|
||||
|
||||
|
|
@ -23,14 +23,14 @@ class Discovery(baseClasses: Set[String], annotations: Set[String])
|
|||
val onClass = findAnnotations(c.annotations)
|
||||
val onDefs = defAnnotations(c.structure.declared) ++ defAnnotations(c.structure.inherited)
|
||||
val module = isModule(c)
|
||||
new Discovered( bases(c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module )
|
||||
new Discovered( bases(c.name, c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module )
|
||||
}
|
||||
def bases(c: Seq[Type]): Set[String] =
|
||||
c.flatMap(simpleName).filter(baseClasses).toSet
|
||||
def bases(own: String, c: Seq[Type]): Set[String] =
|
||||
(own +: c.flatMap(simpleName)).filter(baseClasses).toSet
|
||||
def findAnnotations(as: Seq[Annotation]): Set[String] =
|
||||
as.flatMap { a => simpleName(a.base).filter(annotations) }.toSet
|
||||
def defAnnotations(defs: Seq[Definition]): Set[String] =
|
||||
findAnnotations( defs.flatMap { case d: Def => d.annotations.toSeq; case _ => Nil } )
|
||||
findAnnotations( defs.flatMap { case d: Def if isPublic(d) => d.annotations.toSeq; case _ => Nil } )
|
||||
}
|
||||
object Discovery
|
||||
{
|
||||
|
|
@ -39,6 +39,8 @@ object Discovery
|
|||
val d = new Discovery(subclasses, annotations)
|
||||
d(definitions)
|
||||
}
|
||||
def applications(definitions: Seq[Definition]): Seq[(Definition, Discovered)] =
|
||||
apply(Set.empty, Set.empty)( definitions )
|
||||
|
||||
def isConcrete(a: Definition): Boolean = isConcrete(a.modifiers)
|
||||
def isConcrete(m: Modifiers) = !m.isAbstract && !m.isDeferred
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package xsbt
|
||||
package sbt
|
||||
package compile
|
||||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import org.specs.Specification
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import org.specs.Specification
|
||||
|
||||
/** Verifies that the analyzer plugin properly detects main methods. The main method must be
|
||||
* public with the right signature and be defined on a public, top-level module.*/
|
||||
|
|
@ -104,16 +105,24 @@ object ApplicationsTest extends Specification
|
|||
"Analysis plugin should detect applications" in {
|
||||
WithFiles(sources : _*) { case files @ Seq(main, main2, main3, main4, main5, main6, main7, main8, main9, mainA, mainB, mainC, mainD, mainE, mainF) =>
|
||||
for(scalaVersion <- TestCompile.allVersions)
|
||||
CallbackTest(scalaVersion, files, Nil, Nil) { (callback, file, scalaInstance, log) =>
|
||||
val expected = Seq( main -> "Main", main4 -> "Main4", main8 -> "Main8", main9 -> "Main9", mainB -> "MainB",
|
||||
CallbackTest.full(scalaVersion, files) { (callback, file, scalaInstance, log) =>
|
||||
val expected = Set( main -> "Main", main4 -> "Main4", main8 -> "Main8", main9 -> "Main9", mainB -> "MainB",
|
||||
mainE -> "MainE1", mainE -> "MainE2", mainE -> "MainE3", mainE -> "MainE4", mainE -> "MainE5",
|
||||
mainF -> "MainF1", mainF -> "MainF2", mainF -> "MainF4")
|
||||
(callback.applications) must haveTheSameElementsAs(expected)
|
||||
val actual = applications(callback).toSet
|
||||
(actual -- expected) must beEmpty
|
||||
(expected -- actual) must beEmpty
|
||||
val loader = new URLClassLoader(Array(file.toURI.toURL), scalaInstance.loader)
|
||||
for( (_, className) <- expected) testRun(loader, className)
|
||||
}
|
||||
}
|
||||
}
|
||||
def applications(callback: xsbti.TestCallback): Seq[(File, String)] =
|
||||
for( (file, api) <- callback.apis.toSeq; application <- applications(api))
|
||||
yield (file, application)
|
||||
def applications(src: xsbti.api.Source): Seq[String] =
|
||||
Discovery.applications(src.definitions) collect { case (definition, Discovered(_, _, true, _)) => definition.name }
|
||||
|
||||
private def testRun(loader: ClassLoader, className: String)
|
||||
{
|
||||
val obj = Class.forName(className, true, loader)
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package xsbt
|
||||
package sbt
|
||||
package compile
|
||||
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
|
||||
object DetectAnnotations extends Specification
|
||||
{
|
||||
|
|
@ -32,20 +33,30 @@ object DetectAnnotations extends Specification
|
|||
{
|
||||
case files @ Seq(a, b, c, sup1File, sup2File, sup3File, sup4File, midFile, sub1File, sub2File, sub3File, sub4File, sub5File, sub6File, sub7File, sub8File, sub9File, subAFile, subBFile) =>
|
||||
for(scalaVersion <- TestCompile.allVersions)
|
||||
CallbackTest(scalaVersion, files, Nil, Seq("c.A", "B", "d.C") ) { (callback, _, _, _) =>
|
||||
CallbackTest.simple(scalaVersion, files) { callback =>
|
||||
val expected =
|
||||
(sup3File, "Super3", "B", false) ::
|
||||
(sub3File, "Sub3", "B", false) ::
|
||||
(sub3File, "Sub3", "c.A", false) ::
|
||||
(sub7File, "Sub7", "d.C", false) ::
|
||||
(sub8File, "Sub8", "c.A", false) ::
|
||||
(sub9File, "Sub9", "B", true) ::
|
||||
(subAFile, "SubA", "c.A", true) ::
|
||||
(subBFile, "SubB", "c.A", true) ::
|
||||
(subBFile, "SubB", "d.C", false) ::
|
||||
(sup3File, "Super3", Set("B"), false) ::
|
||||
(sub3File, "Sub3", Set("B", "c.A"), false) ::
|
||||
(sub7File, "Sub7", Set("d.C"), false) ::
|
||||
(sub8File, "Sub8", Set("c.A"), false) ::
|
||||
(sub9File, "Sub9", Set("B"), true) ::
|
||||
(subAFile, "SubA", Set("c.A"), true) ::
|
||||
(subBFile, "SubB", Set("c.A"), true) ::
|
||||
(subBFile, "SubB", Set("d.C"), false) ::
|
||||
Nil
|
||||
(callback.foundAnnotated) must haveTheSameElementsAs(expected)
|
||||
val actual = subclasses(callback).toSet
|
||||
val actualOnly = (actual -- expected)
|
||||
println("Actual: " + actualOnly)
|
||||
val expectedOnly = (expected.toSet -- actual)
|
||||
println("Expected: " + expectedOnly)
|
||||
expectedOnly must beEmpty
|
||||
actualOnly must beEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] =
|
||||
for( (file, src) <- callback.apis.toSeq; (definition, discovered) <- Discovery(Set.empty, annotationNames)(src.definitions) if !discovered.isEmpty ) yield
|
||||
(file, definition.name, discovered.annotations, discovered.isModule)
|
||||
|
||||
def annotationNames = Set("c.A", "B", "d.C")
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package sbt
|
||||
package compile
|
||||
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
|
||||
object DetectSubclasses extends Specification
|
||||
{
|
||||
val sources =
|
||||
("a/Super.scala" -> "package a; trait Super") ::
|
||||
("a/Super2.scala" -> "class Super2") ::
|
||||
("b/Middle.scala" -> "package y.w; trait Mid extends a.Super") ::
|
||||
("b/Sub1.scala" -> "package a; class Sub1 extends y.w.Mid") ::
|
||||
("b/Sub2.scala" -> "final class Sub2 extends a.Super") ::
|
||||
("Sub3.scala" -> "private class F extends a.Super; package c { object Sub3 extends Super2 }") ::
|
||||
Nil
|
||||
|
||||
"Analysis plugin should detect subclasses" in {
|
||||
WithFiles(sources.map{case (file, content) => (new File(file), content)} : _*)
|
||||
{
|
||||
case files @ Seq(supFile, sup2File, midFile, sub1File, sub2File, sub3File) =>
|
||||
for(scalaVersion <- TestCompile.allVersions)
|
||||
CallbackTest.simple(scalaVersion, files) { callback =>
|
||||
val expected =
|
||||
(sub1File, "a.Sub1", Set("a.Super"), false) ::
|
||||
(sub2File, "Sub2", Set("a.Super"), false) ::
|
||||
(sup2File, "Super2", Set("Super2"), false) ::
|
||||
(sub3File, "c.Sub3", Set("Super2"), true) ::
|
||||
Nil
|
||||
val actual = subclasses(callback).toSet
|
||||
val actualOnly = actual -- expected
|
||||
val expectedOnly = expected.toSet -- actual
|
||||
assert(actualOnly.isEmpty, "Actual only: " + actualOnly)
|
||||
assert(expectedOnly.isEmpty , "Expected only: " + expectedOnly)
|
||||
}
|
||||
}
|
||||
}
|
||||
def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] =
|
||||
for( (file, src) <- callback.apis.toSeq; (definition, discovered) <- Discovery(subclassNames, Set.empty)(src.definitions) if !discovered.isEmpty ) yield
|
||||
(file, definition.name, discovered.baseClasses, discovered.isModule)
|
||||
def subclassNames = Set( "a.Super", "Super2", "x.Super3", "Super4")
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package xsbt
|
||||
package sbt
|
||||
package compile
|
||||
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
|
||||
object CheckBasic extends Specification
|
||||
{
|
||||
|
|
@ -21,9 +22,9 @@ object CheckBasic extends Specification
|
|||
WithFiles(basicName -> basicSource){ files =>
|
||||
for(scalaVersion <- TestCompile.allVersions)
|
||||
{
|
||||
FileUtilities.withTemporaryDirectory { outputDirectory =>
|
||||
IO.withTemporaryDirectory { outputDirectory =>
|
||||
WithCompiler(scalaVersion) { (compiler, log) =>
|
||||
compiler.doc(Set() ++ files, Set.empty, outputDirectory, Nil, 5, log)
|
||||
compiler.doc(files.toSeq, Nil, outputDirectory, Nil, 5, log)
|
||||
}
|
||||
}
|
||||
true must beTrue // don't know how to just check that previous line completes without exception
|
||||
|
|
@ -33,7 +34,7 @@ object CheckBasic extends Specification
|
|||
"Analyzer plugin should send source begin and end" in {
|
||||
WithFiles(basicName -> basicSource) { files =>
|
||||
for(scalaVersion <- TestCompile.allVersions)
|
||||
CallbackTest(scalaVersion, files) { callback =>
|
||||
CallbackTest.simple(scalaVersion, files) { callback =>
|
||||
(callback.beganSources) must haveTheSameElementsAs(files)
|
||||
(callback.endedSources) must haveTheSameElementsAs(files)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package xsbt
|
||||
package sbt
|
||||
package compile
|
||||
|
||||
import sbt.{ComponentManager, TestIvyLogger}
|
||||
|
||||
import java.io.File
|
||||
import FileUtilities.withTemporaryDirectory
|
||||
import org.specs._
|
||||
import java.io.File
|
||||
import IO.withTemporaryDirectory
|
||||
import org.specs._
|
||||
|
||||
object CompileTest extends Specification
|
||||
{
|
||||
|
|
@ -15,8 +14,8 @@ object CompileTest extends Specification
|
|||
WithCompiler( "2.7.4" )(testCompileAnalysis)
|
||||
WithCompiler( "2.7.5" )(testCompileAnalysis)
|
||||
WithCompiler( "2.7.7" )(testCompileAnalysis)
|
||||
WithCompiler( "2.8.0.Beta1" )(testCompileAnalysis)
|
||||
WithCompiler( "2.8.0-SNAPSHOT" )(testCompileAnalysis)
|
||||
WithCompiler( "2.8.0" )(testCompileAnalysis)
|
||||
WithCompiler( "2.8.1.RC2" )(testCompileAnalysis)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,16 +23,17 @@ object CompileTest extends Specification
|
|||
"Properly handle classpaths" in {
|
||||
testClasspath("2.7.2")
|
||||
testClasspath("2.7.7")
|
||||
testClasspath("2.8.0.Beta1")
|
||||
testClasspath("2.8.0")
|
||||
testClasspath("2.8.1.RC2")
|
||||
}
|
||||
}
|
||||
|
||||
private def testCompileAnalysis(compiler: AnalyzingCompiler, log: CompileLogger)
|
||||
private def testCompileAnalysis(compiler: AnalyzingCompiler, log: Logger)
|
||||
{
|
||||
WithFiles( new File("Test.scala") -> "object Test" ) { sources =>
|
||||
withTemporaryDirectory { temp =>
|
||||
val callback = new xsbti.TestCallback(Array(), Array())
|
||||
compiler(Set() ++ sources, Set.empty, temp, Nil, callback, 10, log)
|
||||
val callback = new xsbti.TestCallback
|
||||
compiler(sources, Nil, temp, Nil, callback, 10, log)
|
||||
(callback.beganSources) must haveTheSameElementsAs(sources)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,66 +52,31 @@ object CompileTest extends Specification
|
|||
def compiler(autoBoot: Boolean, compilerOnClasspath: Boolean): RawCompiler =
|
||||
new RawCompiler(ScalaInstance(scalaVersion, launch), new ClasspathOptions(autoBoot, compilerOnClasspath, true), log)
|
||||
|
||||
val callback = new xsbti.TestCallback(Array(), Array())
|
||||
val callback = new xsbti.TestCallback
|
||||
|
||||
val standard = compiler(true, true)
|
||||
val noCompiler = compiler(true, false)
|
||||
val fullExplicit = compiler(false, false)
|
||||
|
||||
val fullBoot = "-bootclasspath" :: fullExplicit.compilerArguments.createBootClasspath :: Nil
|
||||
val withCompiler = Set() + noCompiler.scalaInstance.compilerJar
|
||||
val withCompiler = noCompiler.scalaInstance.compilerJar :: Nil
|
||||
|
||||
WithFiles( new File("Test.scala") -> "object Test", new File("Test2.scala") -> UsingCompiler ) { case Array(plain, useCompiler) =>
|
||||
val plainSrcs = Set[File](plain)
|
||||
val compSrcs = Set[File](useCompiler)
|
||||
FileUtilities.withTemporaryDirectory { out =>
|
||||
standard(plainSrcs, Set.empty, out, Nil) //success
|
||||
standard(compSrcs, Set.empty, out, Nil) //success
|
||||
WithFiles( new File("Test.scala") -> "object Test", new File("Test2.scala") -> UsingCompiler ) { case Seq(plain, useCompiler) =>
|
||||
val plainSrcs = Seq[File](plain)
|
||||
val compSrcs = Seq[File](useCompiler)
|
||||
withTemporaryDirectory { out =>
|
||||
standard(plainSrcs, Nil, out, Nil) //success
|
||||
standard(compSrcs, Nil, out, Nil) //success
|
||||
|
||||
noCompiler(plainSrcs, Set.empty, out, Nil) //success
|
||||
shouldFail( noCompiler(compSrcs, Set.empty, out, Nil) )
|
||||
noCompiler(compSrcs, withCompiler, out, Nil) //success
|
||||
noCompiler(plainSrcs, Nil, out, Nil) //success
|
||||
shouldFail( noCompiler(compSrcs, Nil, out, Nil) )
|
||||
noCompiler(compSrcs, withCompiler, out, Nil) //success
|
||||
|
||||
shouldFail( fullExplicit(plainSrcs, Set.empty, out, Nil) )// failure
|
||||
shouldFail( fullExplicit(compSrcs, Set.empty, out, Nil) )// failure
|
||||
fullExplicit(plainSrcs, Set.empty, out, fullBoot) // success
|
||||
shouldFail( fullExplicit(plainSrcs, Nil, out, Nil) )// failure
|
||||
shouldFail( fullExplicit(compSrcs, Nil, out, Nil) )// failure
|
||||
fullExplicit(plainSrcs, Nil, out, fullBoot) // success
|
||||
fullExplicit(compSrcs, withCompiler, out, fullBoot) // success
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
object WithCompiler
|
||||
{
|
||||
def apply[T](scalaVersion: String)(f: (AnalyzingCompiler, CompileLogger) => T): T =
|
||||
{
|
||||
launcher { (launch, log) =>
|
||||
FileUtilities.withTemporaryDirectory { componentDirectory =>
|
||||
val manager = new ComponentManager(xsbt.boot.Locks, new boot.ComponentProvider(componentDirectory), log)
|
||||
val compiler = new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager, log)
|
||||
compiler.newComponentCompiler(log).clearCache(ComponentCompiler.compilerInterfaceID)
|
||||
define(manager, ComponentCompiler.compilerInterfaceSrcID, getResource("CompilerInterface.scala"), getClassResource(classOf[jline.Completor]))
|
||||
define(manager, ComponentCompiler.xsbtiID, getClassResource(classOf[xsbti.AnalysisCallback]))
|
||||
f(compiler, log)
|
||||
}
|
||||
}
|
||||
}
|
||||
def launcher[T](f: (xsbti.Launcher, TestIvyLogger with CompileLogger) => T): T =
|
||||
{
|
||||
val log = new TestIvyLogger with CompileLogger
|
||||
log.setLevel(Level.Debug)
|
||||
log.bufferQuietly {
|
||||
boot.LaunchTest.withLauncher { launch => f(launch, log) }
|
||||
}
|
||||
}
|
||||
def getClassResource(resource: Class[_]): File = FileUtilities.classLocationFile(resource)
|
||||
def getResource(resource: String): File =
|
||||
{
|
||||
val src = getClass.getClassLoader.getResource(resource)
|
||||
if(src ne null) FileUtilities.asFile(src) else error("Resource not found: " + resource)
|
||||
}
|
||||
def define(manager: ComponentManager, id: String, files: File*)
|
||||
{
|
||||
manager.clearCache(id)
|
||||
manager.define(id, files)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
|
||||
object DetectSubclasses extends Specification
|
||||
{
|
||||
val sources =
|
||||
("a/Super.scala" -> "package a; trait Super") ::
|
||||
("a/Super2.scala" -> "class Super2") ::
|
||||
("b/Middle.scala" -> "package y.w; trait Mid extends a.Super") ::
|
||||
("b/Sub1.scala" -> "package a; class Sub1 extends y.w.Mid") ::
|
||||
("b/Sub2.scala" -> "final class Sub2 extends a.Super") ::
|
||||
("Sub3.scala" -> "private class F extends a.Super; package c { object Sub3 extends Super2 }") ::
|
||||
Nil
|
||||
|
||||
"Analysis plugin should detect subclasses" in {
|
||||
WithFiles(sources.map{case (file, content) => (new File(file), content)} : _*)
|
||||
{
|
||||
case files @ Seq(supFile, sup2File, midFile, sub1File, sub2File, sub3File) =>
|
||||
for(scalaVersion <- TestCompile.allVersions)
|
||||
CallbackTest(scalaVersion, files, Seq( "a.Super", "Super2", "x.Super3", "Super4"), Nil ) { (callback, _, _, _) =>
|
||||
val expected =
|
||||
(sub1File, "a.Sub1", "a.Super", false) ::
|
||||
(sub2File, "Sub2", "a.Super", false) ::
|
||||
(sup2File, "Super2", "Super2", false) ::
|
||||
(sub3File, "c.Sub3", "Super2", true) ::
|
||||
Nil
|
||||
(callback.foundSubclasses) must haveTheSameElementsAs(expected)
|
||||
(callback.invalidSuperclasses) must haveTheSameElementsAs(Seq("x.Super3", "Super4"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +1,37 @@
|
|||
package xsbt
|
||||
package sbt
|
||||
package compile
|
||||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import xsbti.TestCallback
|
||||
import FileUtilities.withTemporaryDirectory
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import xsbti.TestCallback
|
||||
import IO.withTemporaryDirectory
|
||||
|
||||
object TestCompile
|
||||
{
|
||||
// skip 2.7.3 and 2.7.4 for speed
|
||||
def allVersions = List("2.7.2", "2.7.5", "2.7.7", "2.8.0.Beta1", "2.8.0-SNAPSHOT")//List("2.7.2", "2.7.3", "2.7.4", "2.7.5", "2.8.0-SNAPSHOT")
|
||||
def allVersions = List("2.8.0")//List("2.7.2", "2.7.5", "2.7.7", "2.8.0", "2.8.1.RC1")
|
||||
/** Tests running the compiler interface with the analyzer plugin with a test callback. The test callback saves all information
|
||||
* that the plugin sends it for post-compile analysis by the provided function.*/
|
||||
def apply[T](scalaVersion: String, sources: Set[File], outputDirectory: File, options: Seq[String], superclassNames: Seq[String], annotationNames: Seq[String])
|
||||
(f: (TestCallback, ScalaInstance, CompileLogger) => T): T =
|
||||
def apply[T](scalaVersion: String, sources: Seq[File], outputDirectory: File, options: Seq[String])
|
||||
(f: (TestCallback, ScalaInstance, Logger) => T): T =
|
||||
{
|
||||
val testCallback = new TestCallback(superclassNames.toArray, annotationNames.toArray)
|
||||
val testCallback = new TestCallback
|
||||
WithCompiler(scalaVersion) { (compiler, log) =>
|
||||
compiler(sources, Set.empty, outputDirectory, options, testCallback, 5, log)
|
||||
compiler(sources, Nil, outputDirectory, options, testCallback, 5, log)
|
||||
f(testCallback, compiler.scalaInstance, log)
|
||||
}
|
||||
}
|
||||
/** Tests running the compiler interface with the analyzer plugin. The provided function is given a ClassLoader that can
|
||||
* load the compiled classes..*/
|
||||
def apply[T](scalaVersion: String, sources: Seq[File])(f: ClassLoader => T): T =
|
||||
CallbackTest.apply(scalaVersion, sources, Nil, Nil){ case (_, outputDir, _, _) => f(new URLClassLoader(Array(outputDir.toURI.toURL))) }
|
||||
CallbackTest.full(scalaVersion, sources){ case (_, outputDir, _, _) => f(new URLClassLoader(Array(outputDir.toURI.toURL))) }
|
||||
}
|
||||
object CallbackTest
|
||||
{
|
||||
def apply[T](scalaVersion: String, sources: Iterable[File])(f: TestCallback => T): T =
|
||||
apply(scalaVersion, sources.toSeq, Nil, Nil){ case (callback, _, _, _) => f(callback) }
|
||||
def apply[T](scalaVersion: String, sources: Seq[File], superclassNames: Seq[String], annotationNames: Seq[String])(f: (TestCallback, File, ScalaInstance, CompileLogger) => T): T =
|
||||
def simple[T](scalaVersion: String, sources: Seq[File])(f: TestCallback => T): T =
|
||||
full(scalaVersion, sources){ case (callback, _, _, _) => f(callback) }
|
||||
def full[T](scalaVersion: String, sources: Seq[File])(f: (TestCallback, File, ScalaInstance, Logger) => T): T =
|
||||
withTemporaryDirectory { outputDir =>
|
||||
TestCompile(scalaVersion, Set() ++ sources, outputDir, Nil, superclassNames, annotationNames) { case (callback, instance, log) => f(callback, outputDir, instance, log) }
|
||||
TestCompile(scalaVersion, sources, outputDir, Nil) { case (callback, instance, log) => f(callback, outputDir, instance, log) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package sbt
|
||||
package compile
|
||||
|
||||
import xsbt.boot
|
||||
import java.io.File
|
||||
import IO.withTemporaryDirectory
|
||||
|
||||
object WithCompiler
|
||||
{
|
||||
def apply[T](scalaVersion: String)(f: (AnalyzingCompiler, Logger) => T): T =
|
||||
{
|
||||
launcher { (launch, log) =>
|
||||
withTemporaryDirectory { componentDirectory =>
|
||||
val manager = new ComponentManager(xsbt.boot.Locks, new boot.ComponentProvider(componentDirectory), log)
|
||||
val compiler = new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager, log)
|
||||
compiler.newComponentCompiler(log).clearCache(ComponentCompiler.compilerInterfaceID)
|
||||
define(manager, ComponentCompiler.compilerInterfaceSrcID, getResource("CompilerInterface.scala"), getClassResource(classOf[jline.Completor]))
|
||||
define(manager, ComponentCompiler.xsbtiID, getClassResource(classOf[xsbti.AnalysisCallback]))
|
||||
f(compiler, log)
|
||||
}
|
||||
}
|
||||
}
|
||||
def launcher[T](f: (xsbti.Launcher, Logger) => T): T =
|
||||
TestLogger { log =>
|
||||
boot.LaunchTest.withLauncher { launch => f(launch, log) }
|
||||
}
|
||||
|
||||
def getClassResource(resource: Class[_]): File = IO.classLocationFile(resource)
|
||||
def getResource(resource: String): File =
|
||||
{
|
||||
val src = getClass.getClassLoader.getResource(resource)
|
||||
if(src ne null) IO.asFile(src) else error("Resource not found: " + resource)
|
||||
}
|
||||
def define(manager: ComponentManager, id: String, files: File*)
|
||||
{
|
||||
manager.clearCache(id)
|
||||
manager.define(id, files)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +1,23 @@
|
|||
package xsbti
|
||||
|
||||
import java.io.File
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import java.io.File
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
class TestCallback(val superclassNames: Array[String], val annotationNames: Array[String]) extends AnalysisCallback
|
||||
class TestCallback extends AnalysisCallback
|
||||
{
|
||||
val invalidSuperclasses = new ArrayBuffer[String]
|
||||
val beganSources = new ArrayBuffer[File]
|
||||
val endedSources = new ArrayBuffer[File]
|
||||
val foundSubclasses = new ArrayBuffer[(File, String, String, Boolean)]
|
||||
val foundAnnotated = new ArrayBuffer[(File, String, String, Boolean)]
|
||||
val sourceDependencies = new ArrayBuffer[(File, File)]
|
||||
val jarDependencies = new ArrayBuffer[(File, File)]
|
||||
val classDependencies = new ArrayBuffer[(File, File)]
|
||||
val productDependencies = new ArrayBuffer[(File, File)]
|
||||
val binaryDependencies = new ArrayBuffer[(File, String, File)]
|
||||
val products = new ArrayBuffer[(File, File)]
|
||||
val applications = new ArrayBuffer[(File, String)]
|
||||
val apis = new ArrayBuffer[(File, xsbti.api.Source)]
|
||||
|
||||
def superclassNotFound(superclassName: String) { invalidSuperclasses += superclassName }
|
||||
def beginSource(source: File) { beganSources += source }
|
||||
def foundSubclass(source: File, subclassName: String, superclassName: String, isModule: Boolean): Unit =
|
||||
foundSubclasses += ((source, subclassName, superclassName, isModule))
|
||||
def foundAnnotated(source: File, className: String, annotationName: String, isModule: Boolean): Unit =
|
||||
foundAnnotated += ((source, className, annotationName, isModule))
|
||||
|
||||
def sourceDependency(dependsOn: File, source: File) { sourceDependencies += ((dependsOn, source)) }
|
||||
def jarDependency(jar: File, source: File) { jarDependencies += ((jar, source)) }
|
||||
def classDependency(clazz: File, source: File) { classDependencies += ((clazz, source)) }
|
||||
def productDependency(clazz: File, source: File) { productDependencies += ((clazz, source)) }
|
||||
def binaryDependency(binary: File, name: String, source: File) { binaryDependencies += ((binary, name, source)) }
|
||||
def generatedClass(source: File, module: File) { products += ((source, module)) }
|
||||
def endSource(source: File) { endedSources += source }
|
||||
def foundApplication(source: File, className: String) { applications += ((source, className)) }
|
||||
def api(source: File, sourceAPI: xsbti.api.Source) = ()
|
||||
|
||||
def api(source: File, sourceAPI: xsbti.api.Source) { apis += ((source, sourceAPI)) }
|
||||
}
|
||||
|
|
@ -77,5 +77,5 @@ object ComponentManagerTest extends Specification
|
|||
private def writeRandomContent(file: File) = IO.write(file, randomString)
|
||||
private def randomString = "asdf"
|
||||
private def withManager[T](f: ComponentManager => T): T =
|
||||
TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(xsbt.boot.Locks, new xsbt.boot.ComponentProvider(temp), logger)) } )
|
||||
TestLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(xsbt.boot.Locks, new xsbt.boot.ComponentProvider(temp), logger)) } )
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package sbt
|
||||
|
||||
class TestIvyLogger extends BufferedLogger(new ConsoleLogger) with IvyLogger
|
||||
object TestIvyLogger
|
||||
{
|
||||
def apply[T](f: IvyLogger => T): T =
|
||||
{
|
||||
val log = new TestIvyLogger
|
||||
log.setLevel(Level.Debug)
|
||||
log.bufferQuietly(f(log))
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
package sbt
|
||||
|
||||
import std._
|
||||
import inc.{Analysis,Discovered,Discovery}
|
||||
import compile.{Discovered,Discovery}
|
||||
import inc.Analysis
|
||||
import TaskExtra._
|
||||
import Configurations.{Compile => CompileConfig, Test => TestConfig, Runtime => RunConfig}
|
||||
import ClasspathProject._
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package build
|
|||
import java.io.File
|
||||
import classpath.ClasspathUtilities.toLoader
|
||||
import ModuleUtilities.getObject
|
||||
import compile.{AnalyzingCompiler, JavaCompiler}
|
||||
import compile.{AnalyzingCompiler, Discovery, JavaCompiler}
|
||||
import inc.Analysis
|
||||
import Path._
|
||||
import GlobFilter._
|
||||
|
|
@ -91,14 +91,14 @@ object Build
|
|||
import Auto.{Annotation, Explicit, Subclass}
|
||||
auto match {
|
||||
case Explicit => if(name.isEmpty) error("No name specified to load explicitly.") else Seq(new ToLoad(name))
|
||||
case Subclass => discover(analysis, module, new inc.Discovery(Set(name), Set.empty))
|
||||
case Annotation => discover(analysis, module, new inc.Discovery(Set.empty, Set(name)))
|
||||
case Subclass => discover(analysis, module, new Discovery(Set(name), Set.empty))
|
||||
case Annotation => discover(analysis, module, new Discovery(Set.empty, Set(name)))
|
||||
}
|
||||
}
|
||||
def discover(analysis: inc.Analysis, command: DiscoverCommand): Seq[ToLoad] =
|
||||
discover(analysis, command.module, command.discovery)
|
||||
|
||||
def discover(analysis: inc.Analysis, module: Option[Boolean], discovery: inc.Discovery): Seq[ToLoad] =
|
||||
def discover(analysis: inc.Analysis, module: Option[Boolean], discovery: Discovery): Seq[ToLoad] =
|
||||
{
|
||||
for(src <- analysis.apis.internal.values.toSeq;
|
||||
(df, found) <- discovery(src.definitions) if !found.isEmpty && moduleMatches(found.isModule, module))
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
package sbt
|
||||
package build
|
||||
|
||||
import java.io.File
|
||||
import java.io.File
|
||||
import compile.Discovery
|
||||
|
||||
sealed trait LoadCommand
|
||||
final case class BinaryLoad(classpath: Seq[File], module: Boolean, name: String) extends LoadCommand
|
||||
|
|
@ -17,7 +18,7 @@ object Auto extends Enumeration
|
|||
}
|
||||
|
||||
final case class CompileCommand(classpath: Seq[File], sources: Seq[File], output: Option[File], options: Seq[String])
|
||||
final case class DiscoverCommand(module: Option[Boolean], discovery: inc.Discovery)
|
||||
final case class DiscoverCommand(module: Option[Boolean], discovery: Discovery)
|
||||
|
||||
final case class ToLoad(name: String, isModule: Boolean = false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
package sbt
|
||||
package build
|
||||
|
||||
import java.io.File
|
||||
import compile.Discovery
|
||||
import java.io.File
|
||||
|
||||
final class ParseException(msg: String) extends RuntimeException(msg)
|
||||
|
||||
|
|
@ -72,7 +73,7 @@ The command has the following syntax:
|
|||
val args = arguments(commandString)
|
||||
val subs = names("sub", args)
|
||||
val annots = names("annot", args)
|
||||
DiscoverCommand(module(args), new inc.Discovery(subs, annots))
|
||||
DiscoverCommand(module(args), new Discovery(subs, annots))
|
||||
}
|
||||
|
||||
def auto(args: Seq[String]): Auto.Value =
|
||||
|
|
|
|||
|
|
@ -66,13 +66,14 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
// Implements the core functionality of detecting and propagating changes incrementally.
|
||||
// Defines the data structures for representing file fingerprints and relationships and the overall source analysis
|
||||
val compileIncrementalSub = testedBase(compilePath / "inc", "Incremental Compiler", collectionSub, apiSub, ioSub)
|
||||
// Searches the source API data structures, currently looks for subclasses and annotations
|
||||
val discoverySub = testedBase(compilePath / "discover", "Discovery", compileIncrementalSub, apiSub)
|
||||
// Persists the incremental data structures using SBinary
|
||||
val compilePersistSub = project(compilePath / "persist", "Persist", new PersistProject(_), compileIncrementalSub, apiSub)
|
||||
// sbt-side interface to compiler. Calls compiler-side interface reflectively
|
||||
val compilerSub = project(compilePath, "Compile", new CompileProject(_),
|
||||
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub, logSub)
|
||||
// Searches the source API data structures, currently looks for subclasses and annotations
|
||||
val discoverySub = project(compilePath / "discover", "Discovery", new DiscoveryProject(_), compileIncrementalSub, apiSub)
|
||||
|
||||
|
||||
// mostly for implementing 'load' command, could perhaps be trimmed and merged into 'main'
|
||||
val buildSub = baseProject("main" / "build", "Project Builder",
|
||||
|
|
@ -216,6 +217,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
{
|
||||
val testInterface = "org.scala-tools.testing" % "test-interface" % "0.5"
|
||||
}
|
||||
class DiscoveryProject(info: ProjectInfo) extends TestedBase(info) with TestWithCompile
|
||||
class CompileProject(info: ProjectInfo) extends Base(info) with TestWithLog with TestWithLaunch
|
||||
{
|
||||
override def testCompileAction = super.testCompileAction dependsOn(compileInterfaceSub.`package`, interfaceSub.`package`)
|
||||
|
|
@ -322,6 +324,9 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
override def projectClasspath(config: Configuration) = Path.emptyPathFinder
|
||||
}
|
||||
}
|
||||
trait TestWithCompile extends TestWith {
|
||||
override def testWithTestClasspath = super.testWithTestClasspath ++ Seq(compilerSub)
|
||||
}
|
||||
trait TestWithIO extends TestWith {
|
||||
override def testWithTestClasspath = super.testWithTestClasspath ++ Seq(ioSub)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package sbt
|
||||
|
||||
object TestLogger
|
||||
{
|
||||
def apply[T](f: Logger => T): T =
|
||||
{
|
||||
val log = new BufferedLogger(ConsoleLogger())
|
||||
log.setLevel(Level.Debug)
|
||||
log.bufferQuietly(f(log))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue