Use jline-terminal-jna for sbt server

The old sbt launcher uses jansi 1.11, which is incompatible with jline3.
To work around this, we can use the jna terminal implementation for the
jline system terminal. This commit also switches to using the jline
TerminalBuilder for all system terminals except for the windows system
terminal with the thin client. The jline terminal builder uses
reflection that is difficult to make work with the thin client and it is
much easier to just manually construct the thin client. This is only
necessary for windows because on posix the thin client will fall back to
an implementation that shells out for stty commands.
This commit is contained in:
Ethan Atkins 2020-09-20 14:56:49 -07:00
parent 09599e4863
commit 410a8dd4b1
3 changed files with 34 additions and 42 deletions

View File

@ -368,6 +368,7 @@ lazy val utilLogging = (project in file("internal") / "util-logging")
Seq(
jline,
jline3Terminal,
jline3JNA,
jline3Jansi,
log4jApi,
log4jCore,

View File

@ -12,12 +12,13 @@ import java.nio.charset.Charset
import java.util.{ Arrays, EnumSet }
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import org.jline.utils.InfoCmp.Capability
import org.jline.utils.{ ClosedException, NonBlockingReader, OSUtils }
import org.jline.utils.{ ClosedException, NonBlockingReader }
import org.jline.terminal.{ Attributes, Size, Terminal => JTerminal }
import org.jline.terminal.Terminal.SignalHandler
import org.jline.terminal.impl.{ AbstractTerminal, DumbTerminal }
import org.jline.terminal.impl.jansi.JansiSupportImpl
import org.jline.terminal.impl.jansi.win.JansiWinSysTerminal
import org.jline.utils.OSUtils
import scala.collection.JavaConverters._
import scala.util.Try
import java.util.concurrent.LinkedBlockingQueue
@ -30,47 +31,36 @@ private[sbt] object JLine3 {
}
.toMap
private[util] def system = {
/*
* For reasons that are unclear to me, TerminalBuilder fails to build
* windows terminals. The instructions about the classpath did not work:
* https://stackoverflow.com/questions/52851232/jline3-issues-with-windows-terminal
* We can deconstruct what TerminalBuilder does and inline it for now.
* It is possible that this workaround will break WSL but I haven't checked that.
*/
if (Util.isNonCygwinWindows) {
val support = new JansiSupportImpl
val winConsole = support.isWindowsConsole();
try {
val term = JansiWinSysTerminal.createTerminal(
"console",
"ansi",
OSUtils.IS_CONEMU,
Charset.forName("UTF-8"),
-1,
false,
SignalHandler.SIG_DFL,
true
)
term.disableScrolling()
term
} catch {
case _: Exception =>
org.jline.terminal.TerminalBuilder
.builder()
.system(false)
.paused(true)
.jansi(true)
.streams(Terminal.console.inputStream, Terminal.console.outputStream)
.build()
}
} else {
private[this] val forceWindowsJansiHolder = new AtomicBoolean(false)
private[sbt] def forceWindowsJansi(): Unit = forceWindowsJansiHolder.set(true)
private[this] def windowsJansi(): org.jline.terminal.Terminal = {
val support = new JansiSupportImpl
val winConsole = support.isWindowsConsole();
val termType = sys.props.get("org.jline.terminal.type").orElse(sys.env.get("TERM")).orNull
val term = JansiWinSysTerminal.createTerminal(
"console",
termType,
OSUtils.IS_CONEMU,
Charset.forName("UTF-8"),
-1,
false,
SignalHandler.SIG_DFL,
true
)
term.disableScrolling()
term
}
private[util] def system: org.jline.terminal.Terminal = {
if (forceWindowsJansiHolder.get) windowsJansi()
else {
// Only use jna on windows. Both jna and jansi use illegal reflective
// accesses on posix system.
org.jline.terminal.TerminalBuilder
.builder()
.system(System.console != null)
.jna(Util.isNonCygwinWindows)
.jansi(false)
.paused(true)
.jna(false)
.jansi(true)
.build()
}
}

View File

@ -64,12 +64,13 @@ public final class MetaBuildLoader extends URLClassLoader {
* library.
*/
public static MetaBuildLoader makeLoader(final AppProvider appProvider) throws IOException {
final Pattern pattern =
Pattern.compile(
"^(test-interface-[0-9.]+|jline-(terminal-)?[0-9.]+-sbt-.*|jansi-[0-9.]+)\\.jar");
final String jlineJars = "jline-(terminal-)?[0-9.]+-sbt-.*|jline-terminal-(jna|jansi)-[0-9.]+";
final String fullPattern =
"^(test-interface-[0-9.]+|" + jlineJars + "|jansi-[0-9.]+|jna-(platform-)?[0-9.]+)\\.jar";
final Pattern pattern = Pattern.compile(fullPattern);
final File[] cp = appProvider.mainClasspath();
final URL[] interfaceURLs = new URL[1];
final URL[] jlineURLs = new URL[3];
final URL[] jlineURLs = new URL[7];
final File[] extra =
appProvider.id().classpathExtra() == null ? new File[0] : appProvider.id().classpathExtra();
final Set<File> bottomClasspath = new LinkedHashSet<>();