mirror of https://github.com/sbt/sbt.git
Use terminal printstream in CheckBuildSources
The build source check is evaluated at times when we can't be completely sure that global logger is pointing at the terminal that initiated the reload (which may be a passive watch client). To work around this, we can inspect the exec to determine which terminal initiated the check and write any output directly to that terminal.
This commit is contained in:
parent
25e83d8fec
commit
9dc3c6b17f
|
|
@ -184,7 +184,8 @@ object MainLoop {
|
||||||
/** This is the main function State transfer function of the sbt command processing. */
|
/** This is the main function State transfer function of the sbt command processing. */
|
||||||
def processCommand(exec: Exec, state: State): State = {
|
def processCommand(exec: Exec, state: State): State = {
|
||||||
val channelName = exec.source map (_.channelName)
|
val channelName = exec.source map (_.channelName)
|
||||||
StandardMain.exchange notifyStatus
|
val exchange = StandardMain.exchange
|
||||||
|
exchange notifyStatus
|
||||||
ExecStatusEvent("Processing", channelName, exec.execId, Vector())
|
ExecStatusEvent("Processing", channelName, exec.execId, Vector())
|
||||||
try {
|
try {
|
||||||
def process(): State = {
|
def process(): State = {
|
||||||
|
|
@ -197,9 +198,9 @@ object MainLoop {
|
||||||
state.put(sbt.Keys.currentTaskProgress, new Keys.TaskProgress(progress))
|
state.put(sbt.Keys.currentTaskProgress, new Keys.TaskProgress(progress))
|
||||||
} else state
|
} else state
|
||||||
}
|
}
|
||||||
StandardMain.exchange.setState(progressState)
|
exchange.setState(progressState)
|
||||||
StandardMain.exchange.setExec(Some(exec))
|
exchange.setExec(Some(exec))
|
||||||
StandardMain.exchange.unprompt(ConsoleUnpromptEvent(exec.source), force = false)
|
exchange.unprompt(ConsoleUnpromptEvent(exec.source), force = false)
|
||||||
val newState = Command.process(exec.commandLine, progressState)
|
val newState = Command.process(exec.commandLine, progressState)
|
||||||
if (exec.execId.fold(true)(!_.startsWith(networkExecPrefix)) &&
|
if (exec.execId.fold(true)(!_.startsWith(networkExecPrefix)) &&
|
||||||
!exec.commandLine.startsWith(networkExecPrefix)) {
|
!exec.commandLine.startsWith(networkExecPrefix)) {
|
||||||
|
|
@ -210,26 +211,25 @@ object MainLoop {
|
||||||
newState.remainingCommands.toVector map (_.commandLine),
|
newState.remainingCommands.toVector map (_.commandLine),
|
||||||
exitCode(newState, state),
|
exitCode(newState, state),
|
||||||
)
|
)
|
||||||
StandardMain.exchange.respondStatus(doneEvent)
|
exchange.respondStatus(doneEvent)
|
||||||
}
|
}
|
||||||
StandardMain.exchange.setExec(None)
|
exchange.setExec(None)
|
||||||
newState.get(sbt.Keys.currentTaskProgress).foreach(_.progress.stop())
|
newState.get(sbt.Keys.currentTaskProgress).foreach(_.progress.stop())
|
||||||
newState.remove(sbt.Keys.currentTaskProgress)
|
newState.remove(sbt.Keys.currentTaskProgress)
|
||||||
}
|
}
|
||||||
state.get(CheckBuildSourcesKey) match {
|
state.get(CheckBuildSourcesKey) match {
|
||||||
case Some(cbs) =>
|
case Some(cbs) =>
|
||||||
if (!cbs.needsReload(state, state.globalLogging.full, exec.commandLine)) process()
|
if (!cbs.needsReload(state, exec)) process()
|
||||||
else {
|
else {
|
||||||
if (exec.commandLine.startsWith(SetTerminal))
|
val isSetTerminal = exec.commandLine.startsWith(SetTerminal)
|
||||||
exec +: Exec("reload", None, None) +: state.remove(CheckBuildSourcesKey)
|
if (isSetTerminal) exec +: Exec("reload", None) +: state.remove(CheckBuildSourcesKey)
|
||||||
else
|
else Exec("reload", None) +: exec +: state.remove(CheckBuildSourcesKey)
|
||||||
Exec("reload", None, None) +: exec +: state.remove(CheckBuildSourcesKey)
|
|
||||||
}
|
}
|
||||||
case _ => process()
|
case _ => process()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case err: JsonRpcResponseError =>
|
case err: JsonRpcResponseError =>
|
||||||
StandardMain.exchange.respondError(err, exec.execId, channelName.map(CommandSource(_)))
|
exchange.respondError(err, exec.execId, channelName.map(CommandSource(_)))
|
||||||
throw err
|
throw err
|
||||||
case err: Throwable =>
|
case err: Throwable =>
|
||||||
val errorEvent = ExecStatusEvent(
|
val errorEvent = ExecStatusEvent(
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ private[sbt] object Continuous extends DeprecatedContinuous {
|
||||||
val (nextFileEvent, cleanupFileMonitor): (
|
val (nextFileEvent, cleanupFileMonitor): (
|
||||||
Int => Option[(Watch.Event, Watch.Action)],
|
Int => Option[(Watch.Event, Watch.Action)],
|
||||||
() => Unit
|
() => Unit
|
||||||
) = getFileEvents(configs, logger, state, commands, fileStampCache)
|
) = getFileEvents(configs, logger, state, commands, fileStampCache, channel.name)
|
||||||
val executor = new WatchExecutor(channel.name)
|
val executor = new WatchExecutor(channel.name)
|
||||||
val nextEvent: Int => Watch.Action =
|
val nextEvent: Int => Watch.Action =
|
||||||
combineInputAndFileEvents(nextInputEvent, nextFileEvent, message, logger, logger, executor)
|
combineInputAndFileEvents(nextInputEvent, nextFileEvent, message, logger, logger, executor)
|
||||||
|
|
@ -420,7 +420,8 @@ private[sbt] object Continuous extends DeprecatedContinuous {
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
state: State,
|
state: State,
|
||||||
commands: Seq[String],
|
commands: Seq[String],
|
||||||
fileStampCache: FileStamp.Cache
|
fileStampCache: FileStamp.Cache,
|
||||||
|
channel: String,
|
||||||
)(implicit extracted: Extracted): (Int => Option[(Watch.Event, Watch.Action)], () => Unit) = {
|
)(implicit extracted: Extracted): (Int => Option[(Watch.Event, Watch.Action)], () => Unit) = {
|
||||||
val trackMetaBuild = configs.forall(_.watchSettings.trackMetaBuild)
|
val trackMetaBuild = configs.forall(_.watchSettings.trackMetaBuild)
|
||||||
val buildGlobs =
|
val buildGlobs =
|
||||||
|
|
@ -554,7 +555,9 @@ private[sbt] object Continuous extends DeprecatedContinuous {
|
||||||
getWatchEvent(forceTrigger = false).flatMap { e =>
|
getWatchEvent(forceTrigger = false).flatMap { e =>
|
||||||
state.get(CheckBuildSources.CheckBuildSourcesKey) match {
|
state.get(CheckBuildSources.CheckBuildSourcesKey) match {
|
||||||
case Some(cbs) =>
|
case Some(cbs) =>
|
||||||
if (cbs.needsReload(state, logger, "")) Some(e -> Watch.Reload) else None
|
if (cbs.needsReload(state, Exec("", Some(CommandSource(channel)))))
|
||||||
|
Some(e -> Watch.Reload)
|
||||||
|
else None
|
||||||
case None =>
|
case None =>
|
||||||
Some(e -> Watch.Reload)
|
Some(e -> Watch.Reload)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ package internal.nio
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
||||||
import sbt.BasicCommandStrings.{ RebootCommand, Shutdown, TerminateAction }
|
import sbt.BasicCommandStrings.{ RebootCommand, SetTerminal, Shutdown, TerminateAction }
|
||||||
import sbt.Keys.{ baseDirectory, pollInterval, state }
|
import sbt.Keys.{ baseDirectory, pollInterval, state }
|
||||||
import sbt.Scope.Global
|
import sbt.Scope.Global
|
||||||
import sbt.SlashSyntax0._
|
import sbt.SlashSyntax0._
|
||||||
import sbt.internal.CommandStrings.LoadProject
|
import sbt.internal.CommandStrings.LoadProject
|
||||||
import sbt.internal.SysProp
|
import sbt.internal.SysProp
|
||||||
import sbt.internal.util.AttributeKey
|
import sbt.internal.util.{ AttributeKey, Terminal }
|
||||||
import sbt.io.syntax._
|
import sbt.io.syntax._
|
||||||
import sbt.nio.FileChanges
|
import sbt.nio.FileChanges
|
||||||
import sbt.nio.FileStamp
|
import sbt.nio.FileStamp
|
||||||
|
|
@ -28,6 +28,7 @@ import sbt.util.Logger
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.duration.{ Deadline => SDeadline, _ }
|
import scala.concurrent.duration.{ Deadline => SDeadline, _ }
|
||||||
|
import scala.io.AnsiColor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to determine whether sbt needs to automatically reload
|
* This class is used to determine whether sbt needs to automatically reload
|
||||||
|
|
@ -103,7 +104,28 @@ private[sbt] class CheckBuildSources extends AutoCloseable {
|
||||||
!resetState
|
!resetState
|
||||||
}
|
}
|
||||||
@inline private def forceCheck = fileTreeRepository.isEmpty
|
@inline private def forceCheck = fileTreeRepository.isEmpty
|
||||||
private[sbt] def needsReload(state: State, logger: Logger, cmd: String) = {
|
private[sbt] def needsReload(
|
||||||
|
state: State,
|
||||||
|
exec: Exec
|
||||||
|
): Boolean = {
|
||||||
|
val isSetTerminal = exec.commandLine.startsWith(SetTerminal)
|
||||||
|
val name =
|
||||||
|
if (isSetTerminal)
|
||||||
|
exec.commandLine.split(s"$SetTerminal ").lastOption.filterNot(_.isEmpty)
|
||||||
|
else exec.source.map(_.channelName)
|
||||||
|
val loggerOrTerminal =
|
||||||
|
name.flatMap(StandardMain.exchange.channelForName(_).map(_.terminal)) match {
|
||||||
|
case Some(t) => Right(t)
|
||||||
|
case _ => Left(state.globalLogging.full)
|
||||||
|
}
|
||||||
|
|
||||||
|
needsReload(state, loggerOrTerminal, exec.commandLine)
|
||||||
|
}
|
||||||
|
private def needsReload(
|
||||||
|
state: State,
|
||||||
|
loggerOrTerminal: Either[Logger, Terminal],
|
||||||
|
cmd: String
|
||||||
|
): Boolean = {
|
||||||
(needCheck(state, cmd) && (forceCheck || needUpdate.compareAndSet(true, false))) && {
|
(needCheck(state, cmd) && (forceCheck || needUpdate.compareAndSet(true, false))) && {
|
||||||
val extracted = Project.extract(state)
|
val extracted = Project.extract(state)
|
||||||
val onChanges = extracted.get(Global / onChangedBuildSource)
|
val onChanges = extracted.get(Global / onChangedBuildSource)
|
||||||
|
|
@ -122,14 +144,24 @@ private[sbt] class CheckBuildSources extends AutoCloseable {
|
||||||
else "")
|
else "")
|
||||||
val prefix = rawPrefix.linesIterator.filterNot(_.trim.isEmpty).mkString("\n")
|
val prefix = rawPrefix.linesIterator.filterNot(_.trim.isEmpty).mkString("\n")
|
||||||
if (onChanges == ReloadOnSourceChanges) {
|
if (onChanges == ReloadOnSourceChanges) {
|
||||||
logger.info(s"$prefix\nReloading sbt...")
|
val msg = s"$prefix\nReloading sbt..."
|
||||||
|
loggerOrTerminal match {
|
||||||
|
case Right(t) => msg.linesIterator.foreach(l => t.printStream.println(s"[info] $l"))
|
||||||
|
case Left(l) => l.info(msg)
|
||||||
|
}
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
val tail = "Apply these changes by running `reload`.\nAutomatically reload the " +
|
val tail = "Apply these changes by running `reload`.\nAutomatically reload the " +
|
||||||
"build when source changes are detected by setting " +
|
"build when source changes are detected by setting " +
|
||||||
"`Global / onChangedBuildSource := ReloadOnSourceChanges`.\nDisable this " +
|
"`Global / onChangedBuildSource := ReloadOnSourceChanges`.\nDisable this " +
|
||||||
"warning by setting `Global / onChangedBuildSource := IgnoreSourceChanges`."
|
"warning by setting `Global / onChangedBuildSource := IgnoreSourceChanges`."
|
||||||
logger.warn(s"$prefix\n$tail")
|
val msg = s"$prefix\n$tail"
|
||||||
|
loggerOrTerminal match {
|
||||||
|
case Right(t) =>
|
||||||
|
val prefix = s"[${Def.withColor("warn", Some(AnsiColor.YELLOW), t.isColorEnabled)}]"
|
||||||
|
msg.linesIterator.foreach(l => t.printStream.println(s"$prefix $l"))
|
||||||
|
case Left(l) => l.warn(msg)
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
case _ => false
|
case _ => false
|
||||||
|
|
@ -160,7 +192,7 @@ private[sbt] object CheckBuildSources {
|
||||||
private[sbt] def needReloadImpl: Def.Initialize[Task[StateTransform]] = Def.task {
|
private[sbt] def needReloadImpl: Def.Initialize[Task[StateTransform]] = Def.task {
|
||||||
val st = state.value
|
val st = state.value
|
||||||
st.get(CheckBuildSourcesKey) match {
|
st.get(CheckBuildSourcesKey) match {
|
||||||
case Some(cbs) if (cbs.needsReload(st, st.globalLogging.full, "")) =>
|
case Some(cbs) if (cbs.needsReload(st, Exec("", None))) =>
|
||||||
StateTransform("reload" :: (_: State))
|
StateTransform("reload" :: (_: State))
|
||||||
case _ => StateTransform(identity)
|
case _ => StateTransform(identity)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue