mirror of https://github.com/sbt/sbt.git
Support System.err in thin client
I noticed that when reloading the build, that certain errors are logged by sbt to System.err. These were not shown to a thin client because we weren't forwarding System.err. This change remedies that. System.err is handled more simply than System.out. We do not put System.err through the progress state because generally System.err is tends to be unbuffered. I had hesitated to add System.err to the Terminal interface at all to give users an escape hatch but I couldn't get project loading to work well with the thin client without it.
This commit is contained in:
parent
d53ebaa686
commit
e82c3405b9
|
|
@ -60,6 +60,12 @@ trait Terminal extends AutoCloseable {
|
||||||
*/
|
*/
|
||||||
def outputStream: OutputStream
|
def outputStream: OutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the error stream for this Terminal.
|
||||||
|
* @return the error stream.
|
||||||
|
*/
|
||||||
|
def errorStream: OutputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the terminal supports ansi characters.
|
* Returns true if the terminal supports ansi characters.
|
||||||
*
|
*
|
||||||
|
|
@ -221,7 +227,7 @@ object Terminal {
|
||||||
if (System.console == null) {
|
if (System.console == null) {
|
||||||
originalOut.close()
|
originalOut.close()
|
||||||
originalIn.close()
|
originalIn.close()
|
||||||
System.err.close()
|
originalErr.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,6 +322,7 @@ object Terminal {
|
||||||
override def lineCount(line: String): Int = t.lineCount(line)
|
override def lineCount(line: String): Int = t.lineCount(line)
|
||||||
override def inputStream: InputStream = t.inputStream
|
override def inputStream: InputStream = t.inputStream
|
||||||
override def outputStream: OutputStream = t.outputStream
|
override def outputStream: OutputStream = t.outputStream
|
||||||
|
override def errorStream: OutputStream = t.errorStream
|
||||||
override def isAnsiSupported: Boolean = t.isAnsiSupported
|
override def isAnsiSupported: Boolean = t.isAnsiSupported
|
||||||
override def isColorEnabled: Boolean = t.isColorEnabled
|
override def isColorEnabled: Boolean = t.isColorEnabled
|
||||||
override def isEchoEnabled: Boolean = t.isEchoEnabled
|
override def isEchoEnabled: Boolean = t.isEchoEnabled
|
||||||
|
|
@ -359,14 +366,17 @@ object Terminal {
|
||||||
|
|
||||||
private[sbt] def withOut[T](out: PrintStream)(f: => T): T = {
|
private[sbt] def withOut[T](out: PrintStream)(f: => T): T = {
|
||||||
val originalOut = System.out
|
val originalOut = System.out
|
||||||
|
val originalErr = System.err
|
||||||
val originalProxyOut = ConsoleOut.getGlobalProxy
|
val originalProxyOut = ConsoleOut.getGlobalProxy
|
||||||
try {
|
try {
|
||||||
ConsoleOut.setGlobalProxy(ConsoleOut.printStreamOut(out))
|
ConsoleOut.setGlobalProxy(ConsoleOut.printStreamOut(out))
|
||||||
System.setOut(out)
|
System.setOut(out)
|
||||||
scala.Console.withOut(out)(f)
|
System.setErr(out)
|
||||||
|
scala.Console.withErr(out)(scala.Console.withOut(out)(f))
|
||||||
} finally {
|
} finally {
|
||||||
ConsoleOut.setGlobalProxy(originalProxyOut)
|
ConsoleOut.setGlobalProxy(originalProxyOut)
|
||||||
System.setOut(originalOut)
|
System.setOut(originalOut)
|
||||||
|
System.setErr(originalErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,6 +389,7 @@ object Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private[this] val originalOut = new LinePrintStream(System.out)
|
private[this] val originalOut = new LinePrintStream(System.out)
|
||||||
|
private[this] val originalErr = System.err
|
||||||
private[this] val originalIn = System.in
|
private[this] val originalIn = System.in
|
||||||
private[sbt] class WriteableInputStream(in: InputStream, name: String)
|
private[sbt] class WriteableInputStream(in: InputStream, name: String)
|
||||||
extends InputStream
|
extends InputStream
|
||||||
|
|
@ -476,9 +487,11 @@ object Terminal {
|
||||||
private[this] def withOut[T](f: => T): T = {
|
private[this] def withOut[T](f: => T): T = {
|
||||||
try {
|
try {
|
||||||
System.setOut(proxyPrintStream)
|
System.setOut(proxyPrintStream)
|
||||||
scala.Console.withOut(proxyOutputStream)(f)
|
System.setErr(proxyErrorStream)
|
||||||
|
scala.Console.withErr(proxyErrorStream)(scala.Console.withOut(proxyOutputStream)(f))
|
||||||
} finally {
|
} finally {
|
||||||
System.setOut(originalOut)
|
System.setOut(originalOut)
|
||||||
|
System.setErr(originalErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private[this] def withIn[T](f: => T): T =
|
private[this] def withIn[T](f: => T): T =
|
||||||
|
|
@ -602,6 +615,15 @@ object Terminal {
|
||||||
private[this] val proxyPrintStream = new LinePrintStream(proxyOutputStream) {
|
private[this] val proxyPrintStream = new LinePrintStream(proxyOutputStream) {
|
||||||
override def toString: String = s"proxyPrintStream($proxyOutputStream)"
|
override def toString: String = s"proxyPrintStream($proxyOutputStream)"
|
||||||
}
|
}
|
||||||
|
private[this] object proxyErrorOutputStream extends OutputStream {
|
||||||
|
private[this] def os: OutputStream = activeTerminal.get().errorStream
|
||||||
|
def write(byte: Int): Unit = os.write(byte)
|
||||||
|
override def write(bytes: Array[Byte]): Unit = write(bytes, 0, bytes.length)
|
||||||
|
override def write(bytes: Array[Byte], offset: Int, len: Int): Unit =
|
||||||
|
os.write(bytes, offset, len)
|
||||||
|
override def flush(): Unit = os.flush()
|
||||||
|
}
|
||||||
|
private[this] val proxyErrorStream = new PrintStream(proxyErrorOutputStream, true)
|
||||||
private[this] lazy val isWindows =
|
private[this] lazy val isWindows =
|
||||||
System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0
|
System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0
|
||||||
private[this] object WrappedSystemIn extends InputStream {
|
private[this] object WrappedSystemIn extends InputStream {
|
||||||
|
|
@ -758,7 +780,7 @@ object Terminal {
|
||||||
val term: jline.Terminal with jline.Terminal2,
|
val term: jline.Terminal with jline.Terminal2,
|
||||||
in: InputStream,
|
in: InputStream,
|
||||||
out: OutputStream
|
out: OutputStream
|
||||||
) extends TerminalImpl(in, out, "console0") {
|
) extends TerminalImpl(in, out, originalErr, "console0") {
|
||||||
private[util] lazy val system = JLine3.system
|
private[util] lazy val system = JLine3.system
|
||||||
private[this] def isCI = sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")
|
private[this] def isCI = sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")
|
||||||
override def getWidth: Int = system.getSize.getColumns
|
override def getWidth: Int = system.getSize.getColumns
|
||||||
|
|
@ -815,6 +837,7 @@ object Terminal {
|
||||||
private[sbt] abstract class TerminalImpl private[sbt] (
|
private[sbt] abstract class TerminalImpl private[sbt] (
|
||||||
val in: InputStream,
|
val in: InputStream,
|
||||||
val out: OutputStream,
|
val out: OutputStream,
|
||||||
|
override val errorStream: OutputStream,
|
||||||
override private[sbt] val name: String
|
override private[sbt] val name: String
|
||||||
) extends Terminal {
|
) extends Terminal {
|
||||||
private[this] val rawMode = new AtomicBoolean(false)
|
private[this] val rawMode = new AtomicBoolean(false)
|
||||||
|
|
@ -907,6 +930,7 @@ object Terminal {
|
||||||
override def isSuccessEnabled: Boolean = false
|
override def isSuccessEnabled: Boolean = false
|
||||||
override def isSupershellEnabled: Boolean = false
|
override def isSupershellEnabled: Boolean = false
|
||||||
override def outputStream: java.io.OutputStream = _ => {}
|
override def outputStream: java.io.OutputStream = _ => {}
|
||||||
|
override def errorStream: java.io.OutputStream = _ => {}
|
||||||
override private[sbt] def getAttributes: Map[String, String] = Map.empty
|
override private[sbt] def getAttributes: Map[String, String] = Map.empty
|
||||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit = {}
|
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit = {}
|
||||||
override private[sbt] def setSize(width: Int, height: Int): Unit = {}
|
override private[sbt] def setSize(width: Int, height: Int): Unit = {}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,10 @@ import Serialization.{
|
||||||
cancelRequest,
|
cancelRequest,
|
||||||
promptChannel,
|
promptChannel,
|
||||||
systemIn,
|
systemIn,
|
||||||
|
systemErr,
|
||||||
systemOut,
|
systemOut,
|
||||||
systemOutFlush,
|
systemOutFlush,
|
||||||
|
systemErrFlush,
|
||||||
terminalCapabilities,
|
terminalCapabilities,
|
||||||
terminalCapabilitiesResponse,
|
terminalCapabilitiesResponse,
|
||||||
terminalPropertiesQuery,
|
terminalPropertiesQuery,
|
||||||
|
|
@ -527,9 +529,19 @@ class NetworkClient(
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
Vector.empty
|
Vector.empty
|
||||||
|
case (`systemErr`, Some(json)) =>
|
||||||
|
Converter.fromJson[Array[Byte]](json) match {
|
||||||
|
case Success(bytes) if bytes.nonEmpty && attached.get =>
|
||||||
|
synchronized(errorStream.write(bytes))
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
Vector.empty
|
||||||
case (`systemOutFlush`, _) =>
|
case (`systemOutFlush`, _) =>
|
||||||
synchronized(printStream.flush())
|
synchronized(printStream.flush())
|
||||||
Vector.empty
|
Vector.empty
|
||||||
|
case (`systemErrFlush`, _) =>
|
||||||
|
synchronized(errorStream.flush())
|
||||||
|
Vector.empty
|
||||||
case (`promptChannel`, _) =>
|
case (`promptChannel`, _) =>
|
||||||
batchMode.set(false)
|
batchMode.set(false)
|
||||||
Vector.empty
|
Vector.empty
|
||||||
|
|
|
||||||
|
|
@ -709,7 +709,24 @@ final class NetworkChannel(
|
||||||
write(java.util.Arrays.copyOfRange(b, off, off + len))
|
write(java.util.Arrays.copyOfRange(b, off, off + len))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private class NetworkTerminal extends TerminalImpl(inputStream, outputStream, name) {
|
private[this] lazy val errorStream: OutputStream = new OutputStream {
|
||||||
|
private[this] val buffer = new LinkedBlockingQueue[Byte]
|
||||||
|
override def write(b: Int): Unit = buffer.synchronized {
|
||||||
|
buffer.put(b.toByte)
|
||||||
|
}
|
||||||
|
override def flush(): Unit = {
|
||||||
|
val list = new java.util.ArrayList[Byte]
|
||||||
|
buffer.synchronized(buffer.drainTo(list))
|
||||||
|
if (!list.isEmpty) jsonRpcNotify(Serialization.systemErr, list.asScala.toSeq)
|
||||||
|
}
|
||||||
|
override def write(b: Array[Byte]): Unit = buffer.synchronized {
|
||||||
|
b.foreach(buffer.put)
|
||||||
|
}
|
||||||
|
override def write(b: Array[Byte], off: Int, len: Int): Unit = {
|
||||||
|
write(java.util.Arrays.copyOfRange(b, off, off + len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private class NetworkTerminal extends TerminalImpl(inputStream, outputStream, errorStream, name) {
|
||||||
private[this] val pending = new AtomicBoolean(false)
|
private[this] val pending = new AtomicBoolean(false)
|
||||||
private[this] val closed = new AtomicBoolean(false)
|
private[this] val closed = new AtomicBoolean(false)
|
||||||
private[this] val properties = new AtomicReference[TerminalPropertiesResponse]
|
private[this] val properties = new AtomicReference[TerminalPropertiesResponse]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ object Serialization {
|
||||||
private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8"
|
private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8"
|
||||||
val systemIn = "sbt/systemIn"
|
val systemIn = "sbt/systemIn"
|
||||||
val systemOut = "sbt/systemOut"
|
val systemOut = "sbt/systemOut"
|
||||||
|
val systemErr = "sbt/systemErr"
|
||||||
val systemOutFlush = "sbt/systemOutFlush"
|
val systemOutFlush = "sbt/systemOutFlush"
|
||||||
|
val systemErrFlush = "sbt/systemErrFlush"
|
||||||
val terminalPropertiesQuery = "sbt/terminalPropertiesQuery"
|
val terminalPropertiesQuery = "sbt/terminalPropertiesQuery"
|
||||||
val terminalPropertiesResponse = "sbt/terminalPropertiesResponse"
|
val terminalPropertiesResponse = "sbt/terminalPropertiesResponse"
|
||||||
val terminalCapabilities = "sbt/terminalCapabilities"
|
val terminalCapabilities = "sbt/terminalCapabilities"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue