mirror of https://github.com/sbt/sbt.git
Filling in logging and making cross-compile work.
This commit is contained in:
parent
e388ffd3a6
commit
8bfb2802fb
|
|
@ -0,0 +1,10 @@
|
|||
package xsbt
|
||||
|
||||
trait CompileLogger extends xsbti.Logger with NotNull
|
||||
{
|
||||
def info(msg: => String)
|
||||
def debug(msg: => String)
|
||||
def warn(msg: => String)
|
||||
def error(msg: => String)
|
||||
def trace(t: => Throwable)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package xsbt
|
||||
|
||||
import xsbti.{AnalysisCallback, Logger}
|
||||
import xsbti.{AnalysisCallback, Logger => xLogger}
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
|
|
@ -12,38 +12,52 @@ import java.net.URLClassLoader
|
|||
|
||||
/** A basic interface to the compiler. It is called in the same virtual machine, but no dependency analysis is done. This
|
||||
* is used, for example, to compile the interface/plugin code.*/
|
||||
class RawCompiler(scalaLoader: ClassLoader)
|
||||
class RawCompiler(scalaLoader: ClassLoader, scalaLibDirectory: File, log: CompileLogger)
|
||||
{
|
||||
def apply(arguments: Seq[String])
|
||||
lazy val scalaVersion = Class.forName("scala.tools.nsc.Properties", true, scalaLoader).getMethod("versionString").invoke(null)
|
||||
def apply(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String]): Unit =
|
||||
apply(sources, classpath, outputDirectory, options, false)
|
||||
def apply(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], compilerOnClasspath: Boolean)
|
||||
{
|
||||
// reflection is required for binary compatibility
|
||||
// The following import ensures there is a compile error if the class name changes,
|
||||
// but it should not be otherwise directly referenced
|
||||
// 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 = CompilerArguments(scalaLibDirectory)(sources, classpath, outputDirectory, options, compilerOnClasspath)
|
||||
log.debug("Vanilla interface to Scala compiler " + scalaVersion + " with arguments: " + arguments.mkString("\n\t", "\n\t", ""))
|
||||
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaLoader)
|
||||
val process = mainClass.getMethod("process", classOf[Array[String]])
|
||||
val realArray: Array[String] = arguments.toArray
|
||||
assert(realArray.getClass eq classOf[Array[String]])
|
||||
process.invoke(null, realArray)
|
||||
checkForFailure(mainClass, arguments.toArray)
|
||||
}
|
||||
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" }
|
||||
}
|
||||
}
|
||||
/** Interface to the compiler that uses the dependency analysis plugin.*/
|
||||
class AnalyzeCompiler(scalaVersion: String, scalaLoader: ClassLoader, manager: ComponentManager) extends NotNull
|
||||
class AnalyzeCompiler(scalaVersion: String, scalaLoader: ClassLoader, scalaLibDirectory: File, manager: ComponentManager) extends NotNull
|
||||
{
|
||||
def this(scalaVersion: String, provider: xsbti.ScalaProvider, manager: ComponentManager) =
|
||||
this(scalaVersion, provider.getScalaLoader(scalaVersion), manager)
|
||||
/** The jar containing the compiled plugin and the compiler interface code. This will be passed to scalac as a compiler plugin
|
||||
* and used to load the class that actually interfaces with Global.*/
|
||||
def apply(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
||||
def apply(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: CompileLogger): Unit =
|
||||
apply(sources, classpath, outputDirectory, options, false, callback, maximumErrors, log)
|
||||
def apply(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], compilerOnClasspath: Boolean,
|
||||
callback: AnalysisCallback, maximumErrors: Int, log: CompileLogger)
|
||||
{
|
||||
val arguments = CompilerArguments(scalaLibDirectory)(sources, classpath, outputDirectory, options, compilerOnClasspath)
|
||||
// this is the instance used to compile the analysis
|
||||
val componentCompiler = new ComponentCompiler(scalaVersion, new RawCompiler(scalaLoader), manager)
|
||||
val componentCompiler = new ComponentCompiler(scalaVersion, new RawCompiler(scalaLoader, scalaLibDirectory, log), manager)
|
||||
log.debug("Getting " + ComponentCompiler.compilerInterfaceID + " from component compiler for Scala " + scalaVersion + " (loader=" + scalaLoader + ")")
|
||||
val interfaceJar = componentCompiler(ComponentCompiler.compilerInterfaceID)
|
||||
val dual = createDualLoader(scalaLoader, 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
|
||||
val runnable = interface.asInstanceOf[{ def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger): Unit }]
|
||||
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)
|
||||
|
|
@ -54,4 +68,10 @@ class AnalyzeCompiler(scalaVersion: String, scalaLoader: ClassLoader, manager: C
|
|||
val notXsbtiFilter = (name: String) => !xsbtiFilter(name)
|
||||
new DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false)
|
||||
}
|
||||
override def toString = "Analyzing compiler (Scala " + scalaVersion + ")"
|
||||
}
|
||||
object AnalyzeCompiler
|
||||
{
|
||||
def apply(scalaVersion: String, provider: xsbti.ScalaProvider, manager: ComponentManager): AnalyzeCompiler =
|
||||
new AnalyzeCompiler(scalaVersion, provider.getScalaLoader(scalaVersion), provider.getScalaHome(scalaVersion), manager)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
object CompilerArguments
|
||||
{
|
||||
def apply(scalaLibDirectory: File)(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], compilerOnClasspath: Boolean): Seq[String] =
|
||||
{
|
||||
val scalaHome = System.getProperty("scala.home")
|
||||
assert((scalaHome eq null) || scalaHome.isEmpty, "'scala.home' should not be set (was " + scalaHome + ")")
|
||||
def abs(files: Set[File]) = files.map(_.getAbsolutePath)
|
||||
val originalBoot = System.getProperty("sun.boot.class.path", "")
|
||||
val newBootPrefix = if(originalBoot.isEmpty) "" else originalBoot + File.pathSeparator
|
||||
val bootClasspathOption = Seq("-bootclasspath", newBootPrefix + scalaLibraryJar(scalaLibDirectory).getAbsolutePath)
|
||||
val cp2 = classpath ++ (if(compilerOnClasspath) scalaCompilerJar(scalaLibDirectory):: Nil else Nil)
|
||||
val classpathOption = Seq("-cp", abs(cp2).mkString(File.pathSeparator) )
|
||||
val outputOption = Seq("-d", outputDirectory.getAbsolutePath)
|
||||
options ++ outputOption ++ bootClasspathOption ++ classpathOption ++ abs(sources)
|
||||
}
|
||||
private def scalaLibraryJar(scalaLibDirectory: File): File = new File(scalaLibDirectory, "scala-library.jar")
|
||||
private def scalaCompilerJar(scalaLibDirectory: File): File = new File(scalaLibDirectory, "scala-compiler.jar")
|
||||
}
|
||||
|
|
@ -41,8 +41,7 @@ class ComponentCompiler(scalaVersion: String, compiler: RawCompiler, manager: Co
|
|||
val (sourceFiles, resources) = extractedSources.partition(_.getName.endsWith(".scala"))
|
||||
withTemporaryDirectory { outputDirectory =>
|
||||
val xsbtiJars = manager.files(xsbtiID)
|
||||
val arguments = Seq("-d", outputDirectory.getAbsolutePath, "-cp", xsbtiJars.mkString(File.pathSeparator)) ++ sourceFiles.toSeq.map(_.getAbsolutePath)
|
||||
compiler(arguments)
|
||||
compiler(Set() ++ sourceFiles, Set() ++ xsbtiJars, outputDirectory, Nil, true)
|
||||
copy(resources x (FileMapper.rebase(dir, outputDirectory)))
|
||||
zip((outputDirectory ***) x (PathMapper.relativeTo(outputDirectory)), targetJar)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,17 @@ class CompilerInterface
|
|||
{
|
||||
def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
||||
{
|
||||
import scala.tools.nsc.{CompilerCommand, FatalError, Global, Settings, reporters, util}
|
||||
import util.FakePos
|
||||
def debug(msg: => String) = log.debug(Message(msg))
|
||||
import scala.tools.nsc.{CompilerCommand, FatalError, Global, Settings, reporters, util}
|
||||
import util.FakePos
|
||||
|
||||
debug("Interfacing (CompilerInterface) with Scala compiler " + scala.tools.nsc.Properties.versionString)
|
||||
|
||||
val reporter = new LoggerReporter(maximumErrors, log)
|
||||
val settings = new Settings(reporter.error)
|
||||
val command = new CompilerCommand(args.toList, settings, error, false)
|
||||
|
||||
val phasesSet = new scala.collection.mutable.HashSet[Any] // 2.7 compatibility
|
||||
object compiler extends Global(command.settings, reporter)
|
||||
{
|
||||
object sbtAnalyzer extends
|
||||
|
|
@ -23,27 +28,35 @@ class CompilerInterface
|
|||
val global: compiler.type = compiler
|
||||
val phaseName = Analyzer.name
|
||||
val runsAfter = List("jvm")
|
||||
override val runsBefore = List("terminal")
|
||||
val runsRightAfter = None
|
||||
}
|
||||
with SubComponent
|
||||
with SubComponent with Compat27
|
||||
{
|
||||
val analyzer = new Analyzer(global, callback)
|
||||
def newPhase(prev: Phase) = analyzer.newPhase(prev)
|
||||
def name = phaseName
|
||||
}
|
||||
override protected def builtInPhaseDescriptors() = (super.builtInPhaseDescriptors ++ Seq(sbtAnalyzer))
|
||||
/*override protected def computeInternalPhases()
|
||||
lazy val pdescriptors = // done this way for compatibility between 2.7 and 2.8
|
||||
{
|
||||
super.computeInternalPhases()
|
||||
phasesSet += sbtAnalyzer
|
||||
}*/
|
||||
val superd = super.phaseDescriptors
|
||||
if(superd.contains(sbtAnalyzer)) superd else ( super.phaseDescriptors ++ Seq(sbtAnalyzer) ).toList
|
||||
}
|
||||
override def phaseDescriptors = pdescriptors
|
||||
trait Compat27 { val runsBefore: List[String] = Nil }
|
||||
}
|
||||
if(!reporter.hasErrors)
|
||||
{
|
||||
val run = new compiler.Run
|
||||
debug(args.mkString("Calling compiler with arguments (CompilerInterface):\n\t", "\n\t", ""))
|
||||
run compile command.files
|
||||
reporter.printSummary()
|
||||
}
|
||||
!reporter.hasErrors
|
||||
reporter.printSummary()
|
||||
if(reporter.hasErrors)
|
||||
{
|
||||
debug("Compilation failed (CompilerInterface)")
|
||||
throw new xsbti.CompileFailed { val arguments = args; override def toString = "Analyzed compilation failed" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,7 @@
|
|||
*/
|
||||
package xsbt
|
||||
|
||||
import xsbti.F0
|
||||
|
||||
object Message
|
||||
{
|
||||
def apply(s: => String) = new F0[String] { def apply() = s }
|
||||
def apply(s: => String) = new xsbti.F0[String] { def apply() = s }
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package xsbt
|
|||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import xsbti.{Logger, TestCallback, TestLogger}
|
||||
import xsbti.TestCallback
|
||||
import FileUtilities.withTemporaryDirectory
|
||||
|
||||
object TestCompile
|
||||
|
|
@ -13,7 +13,8 @@ object TestCompile
|
|||
{
|
||||
val testCallback = new TestCallback(superclassNames.toArray)
|
||||
val i = new CompilerInterface
|
||||
TestLogger { log =>
|
||||
val log = new BufferedLogger(new ConsoleLogger)
|
||||
log.bufferQuietly {
|
||||
i.run(arguments.toArray, testCallback, 5, log)
|
||||
f(testCallback, log)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,12 @@ object CompileTest extends Specification
|
|||
WithCompiler( "2.7.2" )(testCompileAnalysis)
|
||||
}
|
||||
}
|
||||
private def testCompileAnalysis(compiler: AnalyzeCompiler, log: xsbti.Logger)
|
||||
private def testCompileAnalysis(compiler: AnalyzeCompiler, log: CompileLogger)
|
||||
{
|
||||
WithFiles( new File("Test.scala") -> "object Test" ) { sources =>
|
||||
withTemporaryDirectory { temp =>
|
||||
val arguments = "-d" :: temp.getAbsolutePath :: sources.map(_.getAbsolutePath).toList
|
||||
val callback = new xsbti.TestCallback(Array())
|
||||
compiler(arguments, callback, 10, log)
|
||||
compiler(Set() ++ sources, Set.empty, temp, Nil, callback, 10, log)
|
||||
(callback.beganSources) must haveTheSameElementsAs(sources)
|
||||
}
|
||||
}
|
||||
|
|
@ -27,16 +26,19 @@ object CompileTest extends Specification
|
|||
}
|
||||
object WithCompiler
|
||||
{
|
||||
def apply[T](scalaVersion: String)(f: (AnalyzeCompiler, xsbti.Logger) => T): T =
|
||||
def apply[T](scalaVersion: String)(f: (AnalyzeCompiler, CompileLogger) => T): T =
|
||||
{
|
||||
TestIvyLogger { log =>
|
||||
System.setProperty("scala.home", "") // need to make sure scala.home is unset
|
||||
val log = new TestIvyLogger with CompileLogger
|
||||
log.setLevel(Level.Debug)
|
||||
log.bufferQuietly {
|
||||
FileUtilities.withTemporaryDirectory { temp =>
|
||||
val launch = new xsbt.boot.Launch(temp)
|
||||
val sbtVersion = xsbti.Versions.Sbt
|
||||
val manager = new ComponentManager(launch.getSbtHome(sbtVersion, scalaVersion), log)
|
||||
prepare(manager, ComponentCompiler.compilerInterfaceSrcID, "CompilerInterface.scala")
|
||||
prepare(manager, ComponentCompiler.xsbtiID, classOf[xsbti.AnalysisCallback])
|
||||
f(new AnalyzeCompiler(scalaVersion, launch, manager), log)
|
||||
f(AnalyzeCompiler(scalaVersion, launch, manager), log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package xsbti;
|
||||
|
||||
public abstract class CompileFailed extends RuntimeException
|
||||
{
|
||||
public abstract String[] arguments();
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package xsbti
|
||||
|
||||
class TestLogger extends Logger
|
||||
{
|
||||
private val buffer = new scala.collection.mutable.ArrayBuffer[F0[Unit]]
|
||||
def info(msg: F0[String]) = buffer("[info] ", msg)
|
||||
def warn(msg: F0[String]) = buffer("[warn] ", msg)
|
||||
def debug(msg: F0[String]) = buffer("[debug] ", msg)
|
||||
def error(msg: F0[String]) = buffer("[error] ", msg)
|
||||
def verbose(msg: F0[String]) = buffer("[verbose] ", msg)
|
||||
def info(msg: => String) = buffer("[info] ", msg)
|
||||
def warn(msg: => String) = buffer("[warn] ", msg)
|
||||
def debug(msg: => String) = buffer("[debug] ", msg)
|
||||
def error(msg: => String) = buffer("[error] ", msg)
|
||||
def verbose(msg: => String) = buffer("[verbose] ", msg)
|
||||
def show() { buffer.foreach(_()) }
|
||||
def clear() { buffer.clear() }
|
||||
def trace(t: F0[Throwable]) { buffer += f0(t().printStackTrace) }
|
||||
private def buffer(s: String, msg: F0[String]) { buffer(s, msg()) }
|
||||
private def buffer(s: String, msg: => String) { buffer += f0(println(s + msg)) }
|
||||
}
|
||||
object TestLogger
|
||||
{
|
||||
def apply[T](f: Logger => T): T =
|
||||
{
|
||||
val log = new TestLogger
|
||||
try { f(log) }
|
||||
catch { case e: Exception => log.show(); throw e }
|
||||
finally { log.clear() }
|
||||
}
|
||||
def apply[L <: TestLogger, T](newLogger: => L)(f: L => T): T =
|
||||
{
|
||||
val log = newLogger
|
||||
try { f(log) }
|
||||
catch { case e: Exception => log.show(); throw e }
|
||||
finally { log.clear() }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
package xsbt
|
||||
|
||||
import xsbti.TestLogger
|
||||
|
||||
class TestIvyLogger extends TestLogger with IvyLogger
|
||||
class TestIvyLogger extends BufferedLogger(new ConsoleLogger) with IvyLogger { def verbose(msg: => String) = info(msg) }
|
||||
object TestIvyLogger
|
||||
{
|
||||
def apply[T](f: TestIvyLogger => T): T = TestLogger(new TestIvyLogger)(f)
|
||||
def apply[T](f: IvyLogger => T): T =
|
||||
{
|
||||
val log = new TestIvyLogger
|
||||
log.setLevel(Level.Debug)
|
||||
log.bufferQuietly(f(log))
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ class Launch(projectRootDirectory: File, mainClassName: String) extends Launcher
|
|||
import Launch._
|
||||
final def boot(args: Array[String])
|
||||
{
|
||||
System.setProperty("scala.home", "") // avoid errors from mixing Scala versions in the same JVM
|
||||
checkAndLoad(args) match
|
||||
{
|
||||
case e: Exit => System.exit(e.code)
|
||||
|
|
@ -74,7 +75,7 @@ class Launch(projectRootDirectory: File, mainClassName: String) extends Launcher
|
|||
val scalaLoader = getScalaLoader(scalaVersion)
|
||||
createSbtLoader(sbtVersion, scalaVersion, scalaLoader)
|
||||
}
|
||||
|
||||
|
||||
def run(sbtLoader: ClassLoader, mainClassName: String, configuration: SbtConfiguration): MainResult =
|
||||
{
|
||||
val sbtMain = Class.forName(mainClassName, true, sbtLoader)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
val ioSub = project(utilPath / "io", "IO", new IOProject(_), controlSub)
|
||||
val classpathSub = project(utilPath / "classpath", "Classpath", new Base(_))
|
||||
|
||||
val compileInterfaceSub = project(compilePath / "interface", "Compiler Interface Src", new CompilerInterfaceProject(_), interfaceSub)
|
||||
|
||||
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub)
|
||||
val logSub = project(utilPath / "log", "Logging", new Base(_))
|
||||
val logSub = project(utilPath / "log", "Logging", new Base(_), interfaceSub)
|
||||
|
||||
val compileInterfaceSub = project(compilePath / "interface", "Compiler Interface Src", new CompilerInterfaceProject(_), interfaceSub)
|
||||
|
||||
val taskSub = project(tasksPath, "Tasks", new TaskProject(_), controlSub, collectionSub)
|
||||
val cacheSub = project(cachePath, "Cache", new CacheProject(_), taskSub, ioSub)
|
||||
|
|
@ -58,7 +58,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
}
|
||||
class StandardTaskProject(info: ProjectInfo) extends Base(info)
|
||||
{
|
||||
override def testClasspath = super.testClasspath +++ compilerSub.testClasspath
|
||||
override def testClasspath = super.testClasspath +++ compilerSub.testClasspath --- compilerInterfaceClasspath
|
||||
}
|
||||
|
||||
class IOProject(info: ProjectInfo) extends Base(info) with TestDependencies
|
||||
|
|
@ -73,31 +73,40 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
override def scratch = true
|
||||
override def consoleClasspath = testClasspath
|
||||
}
|
||||
class CompileProject(info: ProjectInfo) extends Base(info)
|
||||
class CompileProject(info: ProjectInfo) extends Base(info) with TestWithLog
|
||||
{
|
||||
override def testCompileAction = super.testCompileAction dependsOn(launchSub.testCompile, compileInterfaceSub.`package`, interfaceSub.`package`)
|
||||
// don't include launch interface in published dependencies because it will be provided by launcher
|
||||
override def deliverProjectDependencies = Set(super.deliverProjectDependencies.toSeq : _*) - launchInterfaceSub.projectID
|
||||
override def testClasspath = super.testClasspath +++ launchSub.testClasspath +++ compileInterfaceSub.jarPath +++ interfaceSub.jarPath
|
||||
override def testClasspath = super.testClasspath +++ launchSub.testClasspath +++ compileInterfaceSub.jarPath +++ interfaceSub.jarPath --- compilerInterfaceClasspath
|
||||
override def compileOptions = super.compileOptions ++ Seq(CompileOption("-Xno-varargs-conversion")) //needed for invoking nsc.scala.tools.Main.process(Array[String])
|
||||
}
|
||||
class IvyProject(info: ProjectInfo) extends Base(info) with TestWithIO
|
||||
class IvyProject(info: ProjectInfo) extends Base(info) with TestWithIO with TestWithLog
|
||||
{
|
||||
val ivy = "org.apache.ivy" % "ivy" % "2.0.0"
|
||||
}
|
||||
class InterfaceProject(info: ProjectInfo) extends DefaultProject(info) with ManagedBase
|
||||
class InterfaceProject(info: ProjectInfo) extends DefaultProject(info) with ManagedBase with TestWithLog
|
||||
{
|
||||
// ensure that interfaces are only Java sources and that they cannot reference Scala classes
|
||||
override def mainSources = descendents(mainSourceRoots, "*.java")
|
||||
override def compileOrder = CompileOrder.JavaThenScala
|
||||
}
|
||||
class CompilerInterfaceProject(info: ProjectInfo) extends Base(info) with SourceProject with TestWithIO
|
||||
class CompilerInterfaceProject(info: ProjectInfo) extends Base(info) with SourceProject with TestWithIO with TestWithLog
|
||||
{
|
||||
def xTestClasspath = projectClasspath(Configurations.Test)
|
||||
}
|
||||
trait TestWithIO extends BasicScalaProject
|
||||
{
|
||||
// use IO from tests
|
||||
override def testCompileAction = super.testCompileAction dependsOn(ioSub.testCompile)
|
||||
override def testClasspath = super.testClasspath +++ ioSub.testClasspath
|
||||
}
|
||||
trait TestWithLog extends BasicScalaProject
|
||||
{
|
||||
override def testCompileAction = super.testCompileAction dependsOn(logSub.compile)
|
||||
override def testClasspath = super.testClasspath +++ logSub.compileClasspath
|
||||
}
|
||||
def compilerInterfaceClasspath = compileInterfaceSub.projectClasspath(Configurations.Test)
|
||||
}
|
||||
|
||||
trait SourceProject extends BasicScalaProject
|
||||
|
|
|
|||
|
|
@ -6,17 +6,19 @@ trait Compile extends TrackedTaskDefinition[CompileReport]
|
|||
{
|
||||
val sources: Task[Set[File]]
|
||||
val classpath: Task[Set[File]]
|
||||
val outputDirectory: Task[File]
|
||||
val options: Task[Seq[String]]
|
||||
|
||||
|
||||
val trackedClasspath = Difference.inputs(classpath, FilesInfo.lastModified, cacheFile("classpath"))
|
||||
val trackedSource = Difference.inputs(sources, FilesInfo.hash, cacheFile("sources"))
|
||||
val trackedOptions =
|
||||
{
|
||||
import Cache._
|
||||
new Changed(options.map(_.toList), cacheFile("options"))
|
||||
import Task._
|
||||
new Changed((outputDirectory, options) map ( "-d" :: _.getAbsolutePath :: _.toList), cacheFile("options"))
|
||||
}
|
||||
val invalidation = InvalidateFiles(cacheFile("dependencies/"))
|
||||
|
||||
|
||||
lazy val task = create
|
||||
def create =
|
||||
trackedClasspath { rawClasspathChanges => // detect changes to the classpath (last modified only)
|
||||
|
|
@ -26,40 +28,38 @@ trait Compile extends TrackedTaskDefinition[CompileReport]
|
|||
trackedOptions(newOpts, sameOpts) bind { // detect changes to options
|
||||
case (options, sourceChanges, classpathChanges) =>
|
||||
invalidation( classpathChanges +++ sourceChanges ) { (report, tracking) => // invalidation based on changes
|
||||
compile(sourceChanges, classpathChanges, options, report, tracking)
|
||||
outputDirectory bind { outDir => compile(sourceChanges, classpathChanges, outDir, options, report, tracking) }
|
||||
}
|
||||
}
|
||||
}
|
||||
} dependsOn(sources, options)// raise these dependencies to the top for parallelism
|
||||
|
||||
def compile(sourceChanges: ChangeReport[File], classpathChanges: ChangeReport[File], options: Seq[String], report: InvalidationReport[File], tracking: UpdateTracking[File]): Task[CompileReport]
|
||||
} dependsOn(sources, options, outputDirectory)// raise these dependencies to the top for parallelism
|
||||
|
||||
def compile(sourceChanges: ChangeReport[File], classpathChanges: ChangeReport[File], outputDirectory: File, options: Seq[String], report: InvalidationReport[File], tracking: UpdateTracking[File]): Task[CompileReport]
|
||||
lazy val tracked = getTracked
|
||||
protected def getTracked = Seq(trackedClasspath, trackedSource, trackedOptions, invalidation)
|
||||
}
|
||||
class StandardCompile(val sources: Task[Set[File]], val classpath: Task[Set[File]], val options: Task[Seq[String]],
|
||||
val superclassNames: Task[Set[String]], val compilerTask: Task[AnalyzeCompiler], val cacheDirectory: File, val log: xsbti.Logger) extends Compile
|
||||
class StandardCompile(val sources: Task[Set[File]], val classpath: Task[Set[File]], val outputDirectory: Task[File], val options: Task[Seq[String]],
|
||||
val superclassNames: Task[Set[String]], val compilerTask: Task[AnalyzeCompiler], val cacheDirectory: File, val log: CompileLogger) extends Compile
|
||||
{
|
||||
import Task._
|
||||
import scala.collection.mutable.{ArrayBuffer, Buffer, HashMap, HashSet, Map, Set => mSet}
|
||||
|
||||
|
||||
override def create = super.create dependsOn(superclassNames, compilerTask) // raise these dependencies to the top for parallelism
|
||||
def compile(sourceChanges: ChangeReport[File], classpathChanges: ChangeReport[File], options: Seq[String], report: InvalidationReport[File], tracking: UpdateTracking[File]): Task[CompileReport] =
|
||||
def compile(sourceChanges: ChangeReport[File], classpathChanges: ChangeReport[File], outputDirectory: File,
|
||||
options: Seq[String], report: InvalidationReport[File], tracking: UpdateTracking[File]): Task[CompileReport] =
|
||||
{
|
||||
val sources = report.invalid ** sourceChanges.checked // determine the sources that need recompiling (report.invalid also contains classes and libraries)
|
||||
val classpath = classpathChanges.checked
|
||||
compile(sources, classpath, options, tracking)
|
||||
compile(sources, classpath, outputDirectory, options, tracking)
|
||||
}
|
||||
def compile(sources: Set[File], classpath: Set[File], options: Seq[String], tracking: UpdateTracking[File]): Task[CompileReport] =
|
||||
def compile(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], tracking: UpdateTracking[File]): Task[CompileReport] =
|
||||
{
|
||||
(compilerTask, superclassNames) map { (compiler, superClasses) =>
|
||||
if(!sources.isEmpty)
|
||||
{
|
||||
val callback = new CompileAnalysisCallback(superClasses.toArray, tracking)
|
||||
val classpathString = abs(classpath).mkString(File.pathSeparator)
|
||||
val classpathOption = if(classpathString.isEmpty) Seq.empty else Seq("-cp", classpathString)
|
||||
val arguments = classpathOption ++ options ++ abs(sources).toSeq
|
||||
|
||||
compiler(arguments, callback, 100, log)
|
||||
log.debug("Compile task calling compiler " + compiler)
|
||||
compiler(sources, classpath, outputDirectory, options, callback, 100, log)
|
||||
}
|
||||
val readTracking = tracking.read
|
||||
val applicationSet = new HashSet[String]
|
||||
|
|
@ -71,6 +71,13 @@ class StandardCompile(val sources: Task[Set[File]], val classpath: Task[Set[File
|
|||
def subclasses(superclass: String) = Set() ++ subclassMap.getOrElse(superclass, Nil)
|
||||
val applications = Set() ++ applicationSet
|
||||
val classes = Set() ++ readTracking.allProducts
|
||||
override def toString =
|
||||
{
|
||||
val superStrings = superclasses.map(superC => superC + " >: \n\t\t" + subclasses(superC).mkString("\n\t\t"))
|
||||
val applicationsPart = if(applications.isEmpty) Nil else Seq("Applications") ++ applications
|
||||
val lines = Seq("Compilation Report:", sources.size + " sources", classes.size + " classes") ++ superStrings
|
||||
lines.mkString("\n\t")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ object SyncTest
|
|||
}
|
||||
object CompileTest
|
||||
{
|
||||
def apply(dir: String, scalaVersion: String, opts: Seq[String], supers: Set[String])
|
||||
def apply(dir: String, scalaVersion: String, options: Seq[String], supers: Set[String])
|
||||
{
|
||||
def test()
|
||||
{
|
||||
|
|
@ -41,12 +41,12 @@ object CompileTest
|
|||
val classpath = Task( dir / "lib" * "*.jar" )
|
||||
WithCompiler(scalaVersion) { (compiler, log) =>
|
||||
temp { cacheDir => temp { outDir =>
|
||||
val options = Task(opts ++ Seq("-d", outDir.getAbsolutePath) )
|
||||
val compile = new StandardCompile(sources, classpath, options, Task(supers), Task(compiler), cacheDir, log)
|
||||
TaskRunner(compile.task)
|
||||
readLine("Press enter to continue...")
|
||||
TaskRunner(compile.task)
|
||||
readLine("Press enter to continue...")
|
||||
val compile = new StandardCompile(sources, classpath, Task(outDir), Task(options), Task(supers), Task(compiler), cacheDir, log)
|
||||
println("Result: " + TaskRunner(compile.task))
|
||||
println("Result: " + TaskRunner(compile.task))
|
||||
TaskRunner(compile.clean)
|
||||
println("Result: " + TaskRunner(compile.task))
|
||||
println("Result: " + TaskRunner(compile.task))
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,16 @@ package xsbt
|
|||
import java.net.URL
|
||||
import java.util.Enumeration
|
||||
|
||||
final class NullLoader extends ClassLoader
|
||||
{
|
||||
override final def loadClass(className: String, resolve: Boolean): Class[_] = throw new ClassNotFoundException("No classes can be loaded from the null loader")
|
||||
override def getResource(name: String): URL = null
|
||||
override def getResources(name: String): Enumeration[URL] = null
|
||||
}
|
||||
|
||||
class DifferentLoaders(message: String, val loaderA: ClassLoader, val loaderB: ClassLoader) extends ClassNotFoundException(message)
|
||||
class DualLoader(parentA: ClassLoader, aOnlyClasses: String => Boolean, aOnlyResources: String => Boolean,
|
||||
parentB: ClassLoader, bOnlyClasses: String => Boolean, bOnlyResources: String => Boolean) extends ClassLoader
|
||||
parentB: ClassLoader, bOnlyClasses: String => Boolean, bOnlyResources: String => Boolean) extends ClassLoader(new NullLoader)
|
||||
{
|
||||
def this(parentA: ClassLoader, aOnly: String => Boolean, parentB: ClassLoader, bOnly: String => Boolean) =
|
||||
this(parentA, aOnly, aOnly, parentB, bOnly, bOnly)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
|
||||
/** Implements the level-setting methods of Logger.*/
|
||||
abstract class BasicLogger extends Logger
|
||||
{
|
||||
private var traceEnabledVar = true
|
||||
private var level: Level.Value = Level.Info
|
||||
def getLevel = level
|
||||
def setLevel(newLevel: Level.Value) { level = newLevel }
|
||||
def enableTrace(flag: Boolean) { traceEnabledVar = flag }
|
||||
def traceEnabled = traceEnabledVar
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/** A logger that can buffer the logging done on it and then can flush the buffer
|
||||
* to the delegate logger provided in the constructor. Use 'startRecording' to
|
||||
* start buffering and then 'play' from to flush the buffer to the backing logger.
|
||||
* The logging level set at the time a message is originally logged is used, not
|
||||
* the level at the time 'play' is called.
|
||||
*
|
||||
* This class assumes that it is the only client of the delegate logger.
|
||||
* */
|
||||
class BufferedLogger(delegate: Logger) extends Logger
|
||||
{
|
||||
private[this] val buffer = new ListBuffer[LogEvent]
|
||||
private[this] var recording = false
|
||||
|
||||
/** Enables buffering. */
|
||||
def record() = { recording = true }
|
||||
def buffer[T](f: => T): T =
|
||||
{
|
||||
record()
|
||||
try { f }
|
||||
finally { stopQuietly() }
|
||||
}
|
||||
def bufferQuietly[T](f: => T): T =
|
||||
{
|
||||
record()
|
||||
try
|
||||
{
|
||||
val result = f
|
||||
clear()
|
||||
result
|
||||
}
|
||||
catch { case e => stopQuietly(); throw e }
|
||||
}
|
||||
private def stopQuietly() = try { stop() } catch { case e: Exception => () }
|
||||
|
||||
/** Flushes the buffer to the delegate logger. This method calls logAll on the delegate
|
||||
* so that the messages are written consecutively. The buffer is cleared in the process. */
|
||||
def play() { delegate.logAll(buffer.readOnly); buffer.clear() }
|
||||
/** Clears buffered events and disables buffering. */
|
||||
def clear(): Unit = { buffer.clear(); recording = false }
|
||||
/** Plays buffered events and disables buffering. */
|
||||
def stop() { play(); clear() }
|
||||
|
||||
def setLevel(newLevel: Level.Value)
|
||||
{
|
||||
buffer += new SetLevel(newLevel)
|
||||
delegate.setLevel(newLevel)
|
||||
}
|
||||
def getLevel = delegate.getLevel
|
||||
def traceEnabled = delegate.traceEnabled
|
||||
def enableTrace(flag: Boolean)
|
||||
{
|
||||
buffer += new SetTrace(flag)
|
||||
delegate.enableTrace(flag)
|
||||
}
|
||||
|
||||
def trace(t: => Throwable): Unit =
|
||||
doBufferableIf(traceEnabled, new Trace(t), _.trace(t))
|
||||
def success(message: => String): Unit =
|
||||
doBufferable(Level.Info, new Success(message), _.success(message))
|
||||
def log(level: Level.Value, message: => String): Unit =
|
||||
doBufferable(level, new Log(level, message), _.log(level, message))
|
||||
def logAll(events: Seq[LogEvent]): Unit =
|
||||
if(recording)
|
||||
buffer ++= events
|
||||
else
|
||||
delegate.logAll(events)
|
||||
def control(event: ControlEvent.Value, message: => String): Unit =
|
||||
doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message))
|
||||
private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
|
||||
doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered)
|
||||
private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
|
||||
if(condition)
|
||||
{
|
||||
if(recording)
|
||||
buffer += appendIfBuffered
|
||||
else
|
||||
doUnbuffered(delegate)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
|
||||
object ConsoleLogger
|
||||
{
|
||||
private val formatEnabled = ansiSupported && !formatExplicitlyDisabled
|
||||
|
||||
private[this] def formatExplicitlyDisabled = java.lang.Boolean.getBoolean("sbt.log.noformat")
|
||||
private[this] def ansiSupported =
|
||||
try { jline.Terminal.getTerminal.isANSISupported }
|
||||
catch { case e: Exception => !isWindows }
|
||||
|
||||
private[this] def os = System.getProperty("os.name")
|
||||
private[this] def isWindows = os.toLowerCase.indexOf("windows") >= 0
|
||||
}
|
||||
|
||||
/** A logger that logs to the console. On supported systems, the level labels are
|
||||
* colored. */
|
||||
class ConsoleLogger extends BasicLogger
|
||||
{
|
||||
import ConsoleLogger.formatEnabled
|
||||
def messageColor(level: Level.Value) = Console.RESET
|
||||
def labelColor(level: Level.Value) =
|
||||
level match
|
||||
{
|
||||
case Level.Error => Console.RED
|
||||
case Level.Warn => Console.YELLOW
|
||||
case _ => Console.RESET
|
||||
}
|
||||
def successLabelColor = Console.GREEN
|
||||
def successMessageColor = Console.RESET
|
||||
override def success(message: => String)
|
||||
{
|
||||
if(atLevel(Level.Info))
|
||||
log(successLabelColor, Level.SuccessLabel, successMessageColor, message)
|
||||
}
|
||||
def trace(t: => Throwable): Unit =
|
||||
System.out.synchronized
|
||||
{
|
||||
if(traceEnabled)
|
||||
t.printStackTrace
|
||||
}
|
||||
def log(level: Level.Value, message: => String)
|
||||
{
|
||||
if(atLevel(level))
|
||||
log(labelColor(level), level.toString, messageColor(level), message)
|
||||
}
|
||||
private def setColor(color: String)
|
||||
{
|
||||
if(formatEnabled)
|
||||
System.out.synchronized { System.out.print(color) }
|
||||
}
|
||||
private def log(labelColor: String, label: String, messageColor: String, message: String): Unit =
|
||||
System.out.synchronized
|
||||
{
|
||||
for(line <- message.split("""\n"""))
|
||||
{
|
||||
setColor(Console.RESET)
|
||||
System.out.print('[')
|
||||
setColor(labelColor)
|
||||
System.out.print(label)
|
||||
setColor(Console.RESET)
|
||||
System.out.print("] ")
|
||||
setColor(messageColor)
|
||||
System.out.print(line)
|
||||
setColor(Console.RESET)
|
||||
System.out.println()
|
||||
}
|
||||
}
|
||||
|
||||
def logAll(events: Seq[LogEvent]) = System.out.synchronized { events.foreach(log) }
|
||||
def control(event: ControlEvent.Value, message: => String)
|
||||
{ log(labelColor(Level.Info), Level.Info.toString, Console.BLUE, message) }
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
|
||||
/** An enumeration defining the levels available for logging. A level includes all of the levels
|
||||
* with id larger than its own id. For example, Warn (id=3) includes Error (id=4).*/
|
||||
object Level extends Enumeration with NotNull
|
||||
{
|
||||
val Debug = Value(1, "debug")
|
||||
val Info = Value(2, "info")
|
||||
val Warn = Value(3, "warn")
|
||||
val Error = Value(4, "error")
|
||||
/** Defines the label to use for success messages. A success message is logged at the info level but
|
||||
* uses this label. Because the label for levels is defined in this module, the success
|
||||
* label is also defined here. */
|
||||
val SuccessLabel = "success"
|
||||
|
||||
// added because elements was renamed to iterator in 2.8.0 nightly
|
||||
def levels = Debug :: Info :: Warn :: Error :: Nil
|
||||
/** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */
|
||||
def apply(s: String) = levels.find(s == _.toString)
|
||||
/** Same as apply, defined for use in pattern matching. */
|
||||
private[xsbt] def unapply(s: String) = apply(s)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
|
||||
sealed trait LogEvent extends NotNull
|
||||
final class Success(val msg: String) extends LogEvent
|
||||
final class Log(val level: Level.Value, val msg: String) extends LogEvent
|
||||
final class Trace(val exception: Throwable) extends LogEvent
|
||||
final class SetLevel(val newLevel: Level.Value) extends LogEvent
|
||||
final class SetTrace(val enabled: Boolean) extends LogEvent
|
||||
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
|
||||
|
||||
object ControlEvent extends Enumeration
|
||||
{
|
||||
val Start, Header, Finish = Value
|
||||
}
|
||||
|
|
@ -3,13 +3,14 @@
|
|||
*/
|
||||
package xsbt
|
||||
|
||||
abstract class Logger extends NotNull
|
||||
import xsbti.{Logger => xLogger, F0}
|
||||
abstract class Logger extends xLogger with NotNull
|
||||
{
|
||||
def getLevel: Level.Value
|
||||
def setLevel(newLevel: Level.Value)
|
||||
def enableTrace(flag: Boolean)
|
||||
def traceEnabled: Boolean
|
||||
|
||||
|
||||
def atLevel(level: Level.Value) = level.id >= getLevel.id
|
||||
def trace(t: => Throwable): Unit
|
||||
final def debug(message: => String): Unit = log(Level.Debug, message)
|
||||
|
|
@ -19,7 +20,7 @@ abstract class Logger extends NotNull
|
|||
def success(message: => String): Unit
|
||||
def log(level: Level.Value, message: => String): Unit
|
||||
def control(event: ControlEvent.Value, message: => String): Unit
|
||||
|
||||
|
||||
def logAll(events: Seq[LogEvent]): Unit
|
||||
/** Defined in terms of other methods in Logger and should not be called from them. */
|
||||
final def log(event: LogEvent)
|
||||
|
|
@ -34,38 +35,11 @@ abstract class Logger extends NotNull
|
|||
case c: ControlEvent => control(c.event, c.msg)
|
||||
}
|
||||
}
|
||||
|
||||
def debug(msg: F0[String]): Unit = log(Level.Debug, msg)
|
||||
def warn(msg: F0[String]): Unit = log(Level.Warn, msg)
|
||||
def info(msg: F0[String]): Unit = log(Level.Info, msg)
|
||||
def error(msg: F0[String]): Unit = log(Level.Error, msg)
|
||||
def trace(msg: F0[Throwable]) = trace(msg.apply)
|
||||
def log(level: Level.Value, msg: F0[String]): Unit = log(level, msg.apply)
|
||||
}
|
||||
|
||||
sealed trait LogEvent extends NotNull
|
||||
final class Success(val msg: String) extends LogEvent
|
||||
final class Log(val level: Level.Value, val msg: String) extends LogEvent
|
||||
final class Trace(val exception: Throwable) extends LogEvent
|
||||
final class SetLevel(val newLevel: Level.Value) extends LogEvent
|
||||
final class SetTrace(val enabled: Boolean) extends LogEvent
|
||||
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
|
||||
|
||||
object ControlEvent extends Enumeration
|
||||
{
|
||||
val Start, Header, Finish = Value
|
||||
}
|
||||
|
||||
/** An enumeration defining the levels available for logging. A level includes all of the levels
|
||||
* with id larger than its own id. For example, Warn (id=3) includes Error (id=4).*/
|
||||
object Level extends Enumeration with NotNull
|
||||
{
|
||||
val Debug = Value(1, "debug")
|
||||
val Info = Value(2, "info")
|
||||
val Warn = Value(3, "warn")
|
||||
val Error = Value(4, "error")
|
||||
/** Defines the label to use for success messages. A success message is logged at the info level but
|
||||
* uses this label. Because the label for levels is defined in this module, the success
|
||||
* label is also defined here. */
|
||||
val SuccessLabel = "success"
|
||||
|
||||
// added because elements was renamed to iterator in 2.8.0 nightly
|
||||
def levels = Debug :: Info :: Warn :: Error :: Nil
|
||||
/** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */
|
||||
def apply(s: String) = levels.find(s == _.toString)
|
||||
/** Same as apply, defined for use in pattern matching. */
|
||||
private[xsbt] def unapply(s: String) = apply(s)
|
||||
}
|
||||
Loading…
Reference in New Issue