diff --git a/interface/src/main/java/xsbti/CompileFailed.java b/interface/src/main/java/xsbti/CompileFailed.java new file mode 100644 index 000000000..bb5b2a93a --- /dev/null +++ b/interface/src/main/java/xsbti/CompileFailed.java @@ -0,0 +1,6 @@ +package xsbti; + +public abstract class CompileFailed extends RuntimeException +{ + public abstract String[] arguments(); +} \ No newline at end of file diff --git a/interface/src/test/scala/TestLogger.scala b/interface/src/test/scala/TestLogger.scala deleted file mode 100644 index c8df588f6..000000000 --- a/interface/src/test/scala/TestLogger.scala +++ /dev/null @@ -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() } - } -} \ No newline at end of file diff --git a/util/log/BasicLogger.scala b/util/log/BasicLogger.scala new file mode 100644 index 000000000..23607c8ed --- /dev/null +++ b/util/log/BasicLogger.scala @@ -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 +} diff --git a/util/log/BufferedLogger.scala b/util/log/BufferedLogger.scala new file mode 100644 index 000000000..7e8348f2d --- /dev/null +++ b/util/log/BufferedLogger.scala @@ -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) + } +} \ No newline at end of file diff --git a/util/log/ConsoleLogger.scala b/util/log/ConsoleLogger.scala new file mode 100644 index 000000000..a7217f026 --- /dev/null +++ b/util/log/ConsoleLogger.scala @@ -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) } +} \ No newline at end of file diff --git a/util/log/Level.scala b/util/log/Level.scala new file mode 100644 index 000000000..86abc257d --- /dev/null +++ b/util/log/Level.scala @@ -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) +} \ No newline at end of file diff --git a/util/log/LogEvent.scala b/util/log/LogEvent.scala new file mode 100644 index 000000000..19a5b24db --- /dev/null +++ b/util/log/LogEvent.scala @@ -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 +} \ No newline at end of file diff --git a/util/log/Logger.scala b/util/log/Logger.scala index b5203acb8..153596f6f 100644 --- a/util/log/Logger.scala +++ b/util/log/Logger.scala @@ -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) -} \ No newline at end of file