diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 283e8e251..7d2369351 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -34,6 +34,7 @@ object Keys val extraLoggers = SettingKey[ScopedKey[_] => Seq[AbstractLogger]]("extra-loggers", "A function that provides additional loggers for a given setting.", DSetting) val logManager = SettingKey[LogManager]("log-manager", "The log manager, which creates Loggers for different contexts.", DSetting) val logBuffered = SettingKey[Boolean]("log-buffered", "True if logging should be buffered until work completes.", CSetting) + val sLog = SettingKey[Logger]("setting-logger", "Logger usable by settings during project loading.", CSetting) // Project keys val projectCommand = AttributeKey[Boolean]("project-command", "Marks Commands that were registered for the current Project.", Invisible) diff --git a/main/src/main/scala/sbt/Load.scala b/main/src/main/scala/sbt/Load.scala index c9f8fed20..b08f1be16 100755 --- a/main/src/main/scala/sbt/Load.scala +++ b/main/src/main/scala/sbt/Load.scala @@ -59,6 +59,7 @@ object Load } def injectGlobal(state: State): Seq[Setting[_]] = (appConfiguration in GlobalScope :== state.configuration) +: + LogManager.settingsLogger(state) +: EvaluateTask.injectSettings def defaultWithGlobal(state: State, base: File, rawConfig: sbt.LoadBuildConfiguration, globalBase: File, log: Logger): sbt.LoadBuildConfiguration = { diff --git a/main/src/main/scala/sbt/LogManager.scala b/main/src/main/scala/sbt/LogManager.scala index bba11a839..5e0eda520 100644 --- a/main/src/main/scala/sbt/LogManager.scala +++ b/main/src/main/scala/sbt/LogManager.scala @@ -11,7 +11,7 @@ package sbt import Scope.GlobalScope import MainLogging._ import BasicKeys.explicitGlobalLogLevels - import Keys.{logLevel, logManager, persistLogLevel, persistTraceLevel, state, traceLevel} + import Keys.{logLevel, logManager, persistLogLevel, persistTraceLevel, sLog, state, traceLevel} import scala.Console.{BLUE,RESET} object LogManager @@ -33,7 +33,7 @@ object LogManager withLoggers((task,state) => defaultScreen(console, suppressedMessage(task, state)), extra = extra) def withScreenLogger(mk: (ScopedKey[_], State) => AbstractLogger): LogManager = withLoggers(screen = mk) - + def withLoggers(screen: (ScopedKey[_], State) => AbstractLogger = (sk, s) => defaultScreen(s.globalLogging.console), backed: PrintWriter => AbstractLogger = defaultBacked(), extra: ScopedKey[_] => Seq[AbstractLogger] = _ => Nil): LogManager = new LogManager { @@ -90,6 +90,24 @@ object LogManager s.put(BasicKeys.explicitGlobalLogLevels, flag) private[this] def hasExplicitGlobalLogLevels(s: State): Boolean = State.getBoolean(s, BasicKeys.explicitGlobalLogLevels, default=false) + + private[sbt] def settingsLogger(state: State): Def.Setting[_] = + // strict to avoid retaining a reference to `state` + sLog in GlobalScope :== globalWrapper(state) + + // construct a Logger that delegates to the global logger, but only holds a weak reference + // this is an approximation to the ideal that would invalidate the delegate after loading completes + private[this] def globalWrapper(s: State): Logger = { + val ref = new java.lang.ref.WeakReference(s.globalLogging.full) + new Logger { + private[this] def slog: Logger = Option(ref.get) getOrElse sys.error("Settings logger used after project was loaded.") + + override val ansiCodesSupported = slog.ansiCodesSupported + override def trace(t: => Throwable) = slog.trace(t) + override def success(message: => String) = slog.success(message) + override def log(level: Level.Value, message: => String) = slog.log(level, message) + } + } } trait LogManager