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
|
||||
|
||||
/**
|
||||
* Gets the error stream for this Terminal.
|
||||
* @return the error stream.
|
||||
*/
|
||||
def errorStream: OutputStream
|
||||
|
||||
/**
|
||||
* Returns true if the terminal supports ansi characters.
|
||||
*
|
||||
|
|
@ -221,7 +227,7 @@ object Terminal {
|
|||
if (System.console == null) {
|
||||
originalOut.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 inputStream: InputStream = t.inputStream
|
||||
override def outputStream: OutputStream = t.outputStream
|
||||
override def errorStream: OutputStream = t.errorStream
|
||||
override def isAnsiSupported: Boolean = t.isAnsiSupported
|
||||
override def isColorEnabled: Boolean = t.isColorEnabled
|
||||
override def isEchoEnabled: Boolean = t.isEchoEnabled
|
||||
|
|
@ -359,14 +366,17 @@ object Terminal {
|
|||
|
||||
private[sbt] def withOut[T](out: PrintStream)(f: => T): T = {
|
||||
val originalOut = System.out
|
||||
val originalErr = System.err
|
||||
val originalProxyOut = ConsoleOut.getGlobalProxy
|
||||
try {
|
||||
ConsoleOut.setGlobalProxy(ConsoleOut.printStreamOut(out))
|
||||
System.setOut(out)
|
||||
scala.Console.withOut(out)(f)
|
||||
System.setErr(out)
|
||||
scala.Console.withErr(out)(scala.Console.withOut(out)(f))
|
||||
} finally {
|
||||
ConsoleOut.setGlobalProxy(originalProxyOut)
|
||||
System.setOut(originalOut)
|
||||
System.setErr(originalErr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -379,6 +389,7 @@ object Terminal {
|
|||
}
|
||||
}
|
||||
private[this] val originalOut = new LinePrintStream(System.out)
|
||||
private[this] val originalErr = System.err
|
||||
private[this] val originalIn = System.in
|
||||
private[sbt] class WriteableInputStream(in: InputStream, name: String)
|
||||
extends InputStream
|
||||
|
|
@ -476,9 +487,11 @@ object Terminal {
|
|||
private[this] def withOut[T](f: => T): T = {
|
||||
try {
|
||||
System.setOut(proxyPrintStream)
|
||||
scala.Console.withOut(proxyOutputStream)(f)
|
||||
System.setErr(proxyErrorStream)
|
||||
scala.Console.withErr(proxyErrorStream)(scala.Console.withOut(proxyOutputStream)(f))
|
||||
} finally {
|
||||
System.setOut(originalOut)
|
||||
System.setErr(originalErr)
|
||||
}
|
||||
}
|
||||
private[this] def withIn[T](f: => T): T =
|
||||
|
|
@ -602,6 +615,15 @@ object Terminal {
|
|||
private[this] val proxyPrintStream = new LinePrintStream(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 =
|
||||
System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0
|
||||
private[this] object WrappedSystemIn extends InputStream {
|
||||
|
|
@ -758,7 +780,7 @@ object Terminal {
|
|||
val term: jline.Terminal with jline.Terminal2,
|
||||
in: InputStream,
|
||||
out: OutputStream
|
||||
) extends TerminalImpl(in, out, "console0") {
|
||||
) extends TerminalImpl(in, out, originalErr, "console0") {
|
||||
private[util] lazy val system = JLine3.system
|
||||
private[this] def isCI = sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")
|
||||
override def getWidth: Int = system.getSize.getColumns
|
||||
|
|
@ -815,6 +837,7 @@ object Terminal {
|
|||
private[sbt] abstract class TerminalImpl private[sbt] (
|
||||
val in: InputStream,
|
||||
val out: OutputStream,
|
||||
override val errorStream: OutputStream,
|
||||
override private[sbt] val name: String
|
||||
) extends Terminal {
|
||||
private[this] val rawMode = new AtomicBoolean(false)
|
||||
|
|
@ -907,6 +930,7 @@ object Terminal {
|
|||
override def isSuccessEnabled: Boolean = false
|
||||
override def isSupershellEnabled: Boolean = false
|
||||
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 setAttributes(attributes: Map[String, String]): Unit = {}
|
||||
override private[sbt] def setSize(width: Int, height: Int): Unit = {}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,10 @@ import Serialization.{
|
|||
cancelRequest,
|
||||
promptChannel,
|
||||
systemIn,
|
||||
systemErr,
|
||||
systemOut,
|
||||
systemOutFlush,
|
||||
systemErrFlush,
|
||||
terminalCapabilities,
|
||||
terminalCapabilitiesResponse,
|
||||
terminalPropertiesQuery,
|
||||
|
|
@ -527,9 +529,19 @@ class NetworkClient(
|
|||
case _ =>
|
||||
}
|
||||
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`, _) =>
|
||||
synchronized(printStream.flush())
|
||||
Vector.empty
|
||||
case (`systemErrFlush`, _) =>
|
||||
synchronized(errorStream.flush())
|
||||
Vector.empty
|
||||
case (`promptChannel`, _) =>
|
||||
batchMode.set(false)
|
||||
Vector.empty
|
||||
|
|
|
|||
|
|
@ -709,7 +709,24 @@ final class NetworkChannel(
|
|||
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 closed = new AtomicBoolean(false)
|
||||
private[this] val properties = new AtomicReference[TerminalPropertiesResponse]
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ object Serialization {
|
|||
private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8"
|
||||
val systemIn = "sbt/systemIn"
|
||||
val systemOut = "sbt/systemOut"
|
||||
val systemErr = "sbt/systemErr"
|
||||
val systemOutFlush = "sbt/systemOutFlush"
|
||||
val systemErrFlush = "sbt/systemErrFlush"
|
||||
val terminalPropertiesQuery = "sbt/terminalPropertiesQuery"
|
||||
val terminalPropertiesResponse = "sbt/terminalPropertiesResponse"
|
||||
val terminalCapabilities = "sbt/terminalCapabilities"
|
||||
|
|
|
|||
Loading…
Reference in New Issue