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
|
package xsbt
|
||||||
|
|
||||||
import xsbti.{AnalysisCallback, Logger}
|
import xsbti.{AnalysisCallback, Logger => xLogger}
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLClassLoader
|
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
|
/** 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.*/
|
* 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
|
// reflection is required for binary compatibility
|
||||||
// The following import ensures there is a compile error if the class name changes,
|
// The following imports ensure there is a compile error if the identifiers change,
|
||||||
// but it should not be otherwise directly referenced
|
// but should not be otherwise directly referenced
|
||||||
import scala.tools.nsc.Main
|
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 mainClass = Class.forName("scala.tools.nsc.Main", true, scalaLoader)
|
||||||
val process = mainClass.getMethod("process", classOf[Array[String]])
|
val process = mainClass.getMethod("process", classOf[Array[String]])
|
||||||
val realArray: Array[String] = arguments.toArray
|
val realArray: Array[String] = arguments.toArray
|
||||||
assert(realArray.getClass eq classOf[Array[String]])
|
assert(realArray.getClass eq classOf[Array[String]])
|
||||||
process.invoke(null, realArray)
|
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.*/
|
/** 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) =
|
def apply(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: CompileLogger): Unit =
|
||||||
this(scalaVersion, provider.getScalaLoader(scalaVersion), manager)
|
apply(sources, classpath, outputDirectory, options, false, callback, maximumErrors, log)
|
||||||
/** The jar containing the compiled plugin and the compiler interface code. This will be passed to scalac as a compiler plugin
|
def apply(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], compilerOnClasspath: Boolean,
|
||||||
* and used to load the class that actually interfaces with Global.*/
|
callback: AnalysisCallback, maximumErrors: Int, log: CompileLogger)
|
||||||
def apply(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
|
||||||
{
|
{
|
||||||
|
val arguments = CompilerArguments(scalaLibDirectory)(sources, classpath, outputDirectory, options, compilerOnClasspath)
|
||||||
// this is the instance used to compile the analysis
|
// 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 interfaceJar = componentCompiler(ComponentCompiler.compilerInterfaceID)
|
||||||
val dual = createDualLoader(scalaLoader, getClass.getClassLoader) // this goes to scalaLoader for scala classes and sbtLoader for xsbti classes
|
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 interfaceLoader = new URLClassLoader(Array(interfaceJar.toURI.toURL), dual)
|
||||||
val interface = Class.forName("xsbt.CompilerInterface", true, interfaceLoader).newInstance
|
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: Logger): Unit }]
|
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
|
// 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
|
// so they will be binary compatible across all versions of Scala
|
||||||
runnable.run(arguments.toArray, callback, maximumErrors, log)
|
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)
|
val notXsbtiFilter = (name: String) => !xsbtiFilter(name)
|
||||||
new DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false)
|
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"))
|
val (sourceFiles, resources) = extractedSources.partition(_.getName.endsWith(".scala"))
|
||||||
withTemporaryDirectory { outputDirectory =>
|
withTemporaryDirectory { outputDirectory =>
|
||||||
val xsbtiJars = manager.files(xsbtiID)
|
val xsbtiJars = manager.files(xsbtiID)
|
||||||
val arguments = Seq("-d", outputDirectory.getAbsolutePath, "-cp", xsbtiJars.mkString(File.pathSeparator)) ++ sourceFiles.toSeq.map(_.getAbsolutePath)
|
compiler(Set() ++ sourceFiles, Set() ++ xsbtiJars, outputDirectory, Nil, true)
|
||||||
compiler(arguments)
|
|
||||||
copy(resources x (FileMapper.rebase(dir, outputDirectory)))
|
copy(resources x (FileMapper.rebase(dir, outputDirectory)))
|
||||||
zip((outputDirectory ***) x (PathMapper.relativeTo(outputDirectory)), targetJar)
|
zip((outputDirectory ***) x (PathMapper.relativeTo(outputDirectory)), targetJar)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,17 @@ class CompilerInterface
|
||||||
{
|
{
|
||||||
def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
||||||
{
|
{
|
||||||
import scala.tools.nsc.{CompilerCommand, FatalError, Global, Settings, reporters, util}
|
def debug(msg: => String) = log.debug(Message(msg))
|
||||||
import util.FakePos
|
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 reporter = new LoggerReporter(maximumErrors, log)
|
||||||
val settings = new Settings(reporter.error)
|
val settings = new Settings(reporter.error)
|
||||||
val command = new CompilerCommand(args.toList, settings, error, false)
|
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 compiler extends Global(command.settings, reporter)
|
||||||
{
|
{
|
||||||
object sbtAnalyzer extends
|
object sbtAnalyzer extends
|
||||||
|
|
@ -23,27 +28,35 @@ class CompilerInterface
|
||||||
val global: compiler.type = compiler
|
val global: compiler.type = compiler
|
||||||
val phaseName = Analyzer.name
|
val phaseName = Analyzer.name
|
||||||
val runsAfter = List("jvm")
|
val runsAfter = List("jvm")
|
||||||
|
override val runsBefore = List("terminal")
|
||||||
val runsRightAfter = None
|
val runsRightAfter = None
|
||||||
}
|
}
|
||||||
with SubComponent
|
with SubComponent with Compat27
|
||||||
{
|
{
|
||||||
val analyzer = new Analyzer(global, callback)
|
val analyzer = new Analyzer(global, callback)
|
||||||
def newPhase(prev: Phase) = analyzer.newPhase(prev)
|
def newPhase(prev: Phase) = analyzer.newPhase(prev)
|
||||||
def name = phaseName
|
def name = phaseName
|
||||||
}
|
}
|
||||||
override protected def builtInPhaseDescriptors() = (super.builtInPhaseDescriptors ++ Seq(sbtAnalyzer))
|
lazy val pdescriptors = // done this way for compatibility between 2.7 and 2.8
|
||||||
/*override protected def computeInternalPhases()
|
|
||||||
{
|
{
|
||||||
super.computeInternalPhases()
|
|
||||||
phasesSet += sbtAnalyzer
|
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)
|
if(!reporter.hasErrors)
|
||||||
{
|
{
|
||||||
val run = new compiler.Run
|
val run = new compiler.Run
|
||||||
|
debug(args.mkString("Calling compiler with arguments (CompilerInterface):\n\t", "\n\t", ""))
|
||||||
run compile command.files
|
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
|
package xsbt
|
||||||
|
|
||||||
import xsbti.F0
|
|
||||||
|
|
||||||
object Message
|
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.io.File
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import xsbti.{Logger, TestCallback, TestLogger}
|
import xsbti.TestCallback
|
||||||
import FileUtilities.withTemporaryDirectory
|
import FileUtilities.withTemporaryDirectory
|
||||||
|
|
||||||
object TestCompile
|
object TestCompile
|
||||||
|
|
@ -13,7 +13,8 @@ object TestCompile
|
||||||
{
|
{
|
||||||
val testCallback = new TestCallback(superclassNames.toArray)
|
val testCallback = new TestCallback(superclassNames.toArray)
|
||||||
val i = new CompilerInterface
|
val i = new CompilerInterface
|
||||||
TestLogger { log =>
|
val log = new BufferedLogger(new ConsoleLogger)
|
||||||
|
log.bufferQuietly {
|
||||||
i.run(arguments.toArray, testCallback, 5, log)
|
i.run(arguments.toArray, testCallback, 5, log)
|
||||||
f(testCallback, log)
|
f(testCallback, log)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,12 @@ object CompileTest extends Specification
|
||||||
WithCompiler( "2.7.2" )(testCompileAnalysis)
|
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 =>
|
WithFiles( new File("Test.scala") -> "object Test" ) { sources =>
|
||||||
withTemporaryDirectory { temp =>
|
withTemporaryDirectory { temp =>
|
||||||
val arguments = "-d" :: temp.getAbsolutePath :: sources.map(_.getAbsolutePath).toList
|
|
||||||
val callback = new xsbti.TestCallback(Array())
|
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)
|
(callback.beganSources) must haveTheSameElementsAs(sources)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -27,16 +26,19 @@ object CompileTest extends Specification
|
||||||
}
|
}
|
||||||
object WithCompiler
|
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 =>
|
FileUtilities.withTemporaryDirectory { temp =>
|
||||||
val launch = new xsbt.boot.Launch(temp)
|
val launch = new xsbt.boot.Launch(temp)
|
||||||
val sbtVersion = xsbti.Versions.Sbt
|
val sbtVersion = xsbti.Versions.Sbt
|
||||||
val manager = new ComponentManager(launch.getSbtHome(sbtVersion, scalaVersion), log)
|
val manager = new ComponentManager(launch.getSbtHome(sbtVersion, scalaVersion), log)
|
||||||
prepare(manager, ComponentCompiler.compilerInterfaceSrcID, "CompilerInterface.scala")
|
prepare(manager, ComponentCompiler.compilerInterfaceSrcID, "CompilerInterface.scala")
|
||||||
prepare(manager, ComponentCompiler.xsbtiID, classOf[xsbti.AnalysisCallback])
|
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
|
package xsbt
|
||||||
|
|
||||||
import xsbti.TestLogger
|
class TestIvyLogger extends BufferedLogger(new ConsoleLogger) with IvyLogger { def verbose(msg: => String) = info(msg) }
|
||||||
|
|
||||||
class TestIvyLogger extends TestLogger with IvyLogger
|
|
||||||
object TestIvyLogger
|
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._
|
import Launch._
|
||||||
final def boot(args: Array[String])
|
final def boot(args: Array[String])
|
||||||
{
|
{
|
||||||
|
System.setProperty("scala.home", "") // avoid errors from mixing Scala versions in the same JVM
|
||||||
checkAndLoad(args) match
|
checkAndLoad(args) match
|
||||||
{
|
{
|
||||||
case e: Exit => System.exit(e.code)
|
case e: Exit => System.exit(e.code)
|
||||||
|
|
@ -74,7 +75,7 @@ class Launch(projectRootDirectory: File, mainClassName: String) extends Launcher
|
||||||
val scalaLoader = getScalaLoader(scalaVersion)
|
val scalaLoader = getScalaLoader(scalaVersion)
|
||||||
createSbtLoader(sbtVersion, scalaVersion, scalaLoader)
|
createSbtLoader(sbtVersion, scalaVersion, scalaLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(sbtLoader: ClassLoader, mainClassName: String, configuration: SbtConfiguration): MainResult =
|
def run(sbtLoader: ClassLoader, mainClassName: String, configuration: SbtConfiguration): MainResult =
|
||||||
{
|
{
|
||||||
val sbtMain = Class.forName(mainClassName, true, sbtLoader)
|
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 ioSub = project(utilPath / "io", "IO", new IOProject(_), controlSub)
|
||||||
val classpathSub = project(utilPath / "classpath", "Classpath", new Base(_))
|
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 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 taskSub = project(tasksPath, "Tasks", new TaskProject(_), controlSub, collectionSub)
|
||||||
val cacheSub = project(cachePath, "Cache", new CacheProject(_), taskSub, ioSub)
|
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)
|
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
|
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 scratch = true
|
||||||
override def consoleClasspath = testClasspath
|
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`)
|
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
|
// 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 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])
|
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"
|
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
|
// ensure that interfaces are only Java sources and that they cannot reference Scala classes
|
||||||
override def mainSources = descendents(mainSourceRoots, "*.java")
|
override def mainSources = descendents(mainSourceRoots, "*.java")
|
||||||
override def compileOrder = CompileOrder.JavaThenScala
|
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
|
trait TestWithIO extends BasicScalaProject
|
||||||
{
|
{
|
||||||
// use IO from tests
|
// use IO from tests
|
||||||
override def testCompileAction = super.testCompileAction dependsOn(ioSub.testCompile)
|
override def testCompileAction = super.testCompileAction dependsOn(ioSub.testCompile)
|
||||||
override def testClasspath = super.testClasspath +++ ioSub.testClasspath
|
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
|
trait SourceProject extends BasicScalaProject
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,19 @@ trait Compile extends TrackedTaskDefinition[CompileReport]
|
||||||
{
|
{
|
||||||
val sources: Task[Set[File]]
|
val sources: Task[Set[File]]
|
||||||
val classpath: Task[Set[File]]
|
val classpath: Task[Set[File]]
|
||||||
|
val outputDirectory: Task[File]
|
||||||
val options: Task[Seq[String]]
|
val options: Task[Seq[String]]
|
||||||
|
|
||||||
val trackedClasspath = Difference.inputs(classpath, FilesInfo.lastModified, cacheFile("classpath"))
|
val trackedClasspath = Difference.inputs(classpath, FilesInfo.lastModified, cacheFile("classpath"))
|
||||||
val trackedSource = Difference.inputs(sources, FilesInfo.hash, cacheFile("sources"))
|
val trackedSource = Difference.inputs(sources, FilesInfo.hash, cacheFile("sources"))
|
||||||
val trackedOptions =
|
val trackedOptions =
|
||||||
{
|
{
|
||||||
import Cache._
|
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/"))
|
val invalidation = InvalidateFiles(cacheFile("dependencies/"))
|
||||||
|
|
||||||
lazy val task = create
|
lazy val task = create
|
||||||
def create =
|
def create =
|
||||||
trackedClasspath { rawClasspathChanges => // detect changes to the classpath (last modified only)
|
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
|
trackedOptions(newOpts, sameOpts) bind { // detect changes to options
|
||||||
case (options, sourceChanges, classpathChanges) =>
|
case (options, sourceChanges, classpathChanges) =>
|
||||||
invalidation( classpathChanges +++ sourceChanges ) { (report, tracking) => // invalidation based on changes
|
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
|
} dependsOn(sources, options, outputDirectory)// 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]
|
||||||
lazy val tracked = getTracked
|
lazy val tracked = getTracked
|
||||||
protected def getTracked = Seq(trackedClasspath, trackedSource, trackedOptions, invalidation)
|
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]],
|
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: xsbti.Logger) extends Compile
|
val superclassNames: Task[Set[String]], val compilerTask: Task[AnalyzeCompiler], val cacheDirectory: File, val log: CompileLogger) extends Compile
|
||||||
{
|
{
|
||||||
import Task._
|
import Task._
|
||||||
import scala.collection.mutable.{ArrayBuffer, Buffer, HashMap, HashSet, Map, Set => mSet}
|
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
|
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 sources = report.invalid ** sourceChanges.checked // determine the sources that need recompiling (report.invalid also contains classes and libraries)
|
||||||
val classpath = classpathChanges.checked
|
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) =>
|
(compilerTask, superclassNames) map { (compiler, superClasses) =>
|
||||||
if(!sources.isEmpty)
|
if(!sources.isEmpty)
|
||||||
{
|
{
|
||||||
val callback = new CompileAnalysisCallback(superClasses.toArray, tracking)
|
val callback = new CompileAnalysisCallback(superClasses.toArray, tracking)
|
||||||
val classpathString = abs(classpath).mkString(File.pathSeparator)
|
log.debug("Compile task calling compiler " + compiler)
|
||||||
val classpathOption = if(classpathString.isEmpty) Seq.empty else Seq("-cp", classpathString)
|
compiler(sources, classpath, outputDirectory, options, callback, 100, log)
|
||||||
val arguments = classpathOption ++ options ++ abs(sources).toSeq
|
|
||||||
|
|
||||||
compiler(arguments, callback, 100, log)
|
|
||||||
}
|
}
|
||||||
val readTracking = tracking.read
|
val readTracking = tracking.read
|
||||||
val applicationSet = new HashSet[String]
|
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)
|
def subclasses(superclass: String) = Set() ++ subclassMap.getOrElse(superclass, Nil)
|
||||||
val applications = Set() ++ applicationSet
|
val applications = Set() ++ applicationSet
|
||||||
val classes = Set() ++ readTracking.allProducts
|
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
|
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()
|
def test()
|
||||||
{
|
{
|
||||||
|
|
@ -41,12 +41,12 @@ object CompileTest
|
||||||
val classpath = Task( dir / "lib" * "*.jar" )
|
val classpath = Task( dir / "lib" * "*.jar" )
|
||||||
WithCompiler(scalaVersion) { (compiler, log) =>
|
WithCompiler(scalaVersion) { (compiler, log) =>
|
||||||
temp { cacheDir => temp { outDir =>
|
temp { cacheDir => temp { outDir =>
|
||||||
val options = Task(opts ++ Seq("-d", outDir.getAbsolutePath) )
|
val compile = new StandardCompile(sources, classpath, Task(outDir), Task(options), Task(supers), Task(compiler), cacheDir, log)
|
||||||
val compile = new StandardCompile(sources, classpath, options, Task(supers), Task(compiler), cacheDir, log)
|
println("Result: " + TaskRunner(compile.task))
|
||||||
TaskRunner(compile.task)
|
println("Result: " + TaskRunner(compile.task))
|
||||||
readLine("Press enter to continue...")
|
TaskRunner(compile.clean)
|
||||||
TaskRunner(compile.task)
|
println("Result: " + TaskRunner(compile.task))
|
||||||
readLine("Press enter to continue...")
|
println("Result: " + TaskRunner(compile.task))
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,16 @@ package xsbt
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.Enumeration
|
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 DifferentLoaders(message: String, val loaderA: ClassLoader, val loaderB: ClassLoader) extends ClassNotFoundException(message)
|
||||||
class DualLoader(parentA: ClassLoader, aOnlyClasses: String => Boolean, aOnlyResources: String => Boolean,
|
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) =
|
def this(parentA: ClassLoader, aOnly: String => Boolean, parentB: ClassLoader, bOnly: String => Boolean) =
|
||||||
this(parentA, aOnly, aOnly, parentB, bOnly, bOnly)
|
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
|
package xsbt
|
||||||
|
|
||||||
abstract class Logger extends NotNull
|
import xsbti.{Logger => xLogger, F0}
|
||||||
|
abstract class Logger extends xLogger with NotNull
|
||||||
{
|
{
|
||||||
def getLevel: Level.Value
|
def getLevel: Level.Value
|
||||||
def setLevel(newLevel: Level.Value)
|
def setLevel(newLevel: Level.Value)
|
||||||
def enableTrace(flag: Boolean)
|
def enableTrace(flag: Boolean)
|
||||||
def traceEnabled: Boolean
|
def traceEnabled: Boolean
|
||||||
|
|
||||||
def atLevel(level: Level.Value) = level.id >= getLevel.id
|
def atLevel(level: Level.Value) = level.id >= getLevel.id
|
||||||
def trace(t: => Throwable): Unit
|
def trace(t: => Throwable): Unit
|
||||||
final def debug(message: => String): Unit = log(Level.Debug, message)
|
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 success(message: => String): Unit
|
||||||
def log(level: Level.Value, message: => String): Unit
|
def log(level: Level.Value, message: => String): Unit
|
||||||
def control(event: ControlEvent.Value, message: => String): Unit
|
def control(event: ControlEvent.Value, message: => String): Unit
|
||||||
|
|
||||||
def logAll(events: Seq[LogEvent]): Unit
|
def logAll(events: Seq[LogEvent]): Unit
|
||||||
/** Defined in terms of other methods in Logger and should not be called from them. */
|
/** Defined in terms of other methods in Logger and should not be called from them. */
|
||||||
final def log(event: LogEvent)
|
final def log(event: LogEvent)
|
||||||
|
|
@ -34,38 +35,11 @@ abstract class Logger extends NotNull
|
||||||
case c: ControlEvent => control(c.event, c.msg)
|
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