Merge pull request #6114 from eatkins/bsp-consolidate

Use NetworkClient to implement `sbt -bsp`
This commit is contained in:
Ethan Atkins 2020-11-20 07:51:11 -08:00 committed by GitHub
commit 37ffbf8ddd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 79 deletions

View File

@ -7,20 +7,12 @@
package sbt.internal.client
import java.io.{ File, InputStream, OutputStream }
import java.io.{ InputStream, OutputStream }
import java.net.Socket
import java.util.concurrent.atomic.AtomicBoolean
import sbt.Exit
import sbt.io.syntax._
import sbt.protocol.ClientSocket
import scala.util.control.NonFatal
import java.lang.ProcessBuilder.Redirect
class BspClient private (sbtServer: Socket) {
private def run(): Exit = Exit(BspClient.bspRun(sbtServer))
}
object BspClient {
private[sbt] def bspRun(sbtServer: Socket): Int = {
@ -72,52 +64,6 @@ object BspClient {
thread
}
def run(configuration: xsbti.AppConfiguration): Exit = {
val baseDirectory = configuration.baseDirectory
val portFile = baseDirectory / "project" / "target" / "active.json"
try {
if (!portFile.exists) {
forkServer(baseDirectory, portFile)
}
val (socket, _) = ClientSocket.socket(portFile)
new BspClient(socket).run()
} catch {
case NonFatal(_) => Exit(1)
}
}
/**
* Forks another instance of sbt in the background.
* This instance must be shutdown explicitly via `sbt -client shutdown`
*/
def forkServer(baseDirectory: File, portfile: File): Unit = {
val args = List("--detach-stdio")
val launchOpts = List(
"-Dfile.encoding=UTF-8",
"-Dsbt.io.virtual=true",
"-Xms1024M",
"-Xmx1024M",
"-Xss4M",
"-XX:ReservedCodeCacheSize=128m"
)
val launcherJarString = sys.props.get("java.class.path") match {
case Some(cp) =>
cp.split(File.pathSeparator)
.headOption
.getOrElse(sys.error("launcher JAR classpath not found"))
case _ => sys.error("property java.class.path expected")
}
val cmd = "java" :: launchOpts ::: "-jar" :: launcherJarString :: args
val processBuilder =
new ProcessBuilder(cmd: _*)
.directory(baseDirectory)
.redirectInput(Redirect.PIPE)
val process = processBuilder.start()
while (process.isAlive && !portfile.exists) Thread.sleep(100)
if (!process.isAlive) sys.error("sbt server exited")
Exit(NetworkClient.run(configuration, configuration.arguments.toList, redirectOutput = true))
}
}

View File

@ -1093,16 +1093,20 @@ object NetworkClient {
terminal: Terminal,
useJNI: Boolean
): Int = {
val printStream = if (args.bsp) errorStream else terminal.printStream
val client =
simpleClient(
args.withBaseDirectory(baseDirectory),
inputStream,
printStream,
errorStream,
useJNI,
terminal
)
clientImpl(client, args.bsp)
}
private def clientImpl(client: NetworkClient, isBsp: Boolean): Int = {
try {
if (args.bsp) {
if (isBsp) {
val (socket, _) =
client.connectOrStartServerAndConnect(promptCompleteUsers = false, retry = true)
BspClient.bspRun(socket)
@ -1214,16 +1218,18 @@ object NetworkClient {
}
def run(configuration: xsbti.AppConfiguration, arguments: List[String]): Int =
try {
val client = new NetworkClient(configuration, parseArgs(arguments.toArray))
try {
if (client.connect(log = true, promptCompleteUsers = false)) client.run()
else 1
} catch { case _: Throwable => 1 } finally client.close()
} catch {
case NonFatal(e) =>
e.printStackTrace()
1
}
run(configuration, arguments, false)
def run(
configuration: xsbti.AppConfiguration,
arguments: List[String],
redirectOutput: Boolean
): Int = {
val term = Terminal.console
val err = new PrintStream(term.errorStream)
val out = if (redirectOutput) err else new PrintStream(term.outputStream)
val args = parseArgs(arguments.toArray).withBaseDirectory(configuration.baseDirectory)
val client = simpleClient(args, term.inputStream, out, err, useJNI = false)
clientImpl(client, args.bsp)
}
private class AccessDeniedException extends Throwable
}

View File

@ -64,10 +64,6 @@ private[sbt] object xMain {
import sbt.internal.CommandStrings.{ BootCommand, DefaultsCommand, InitCommand }
import sbt.internal.client.NetworkClient
val bootServerSocket = getSocketOrExit(configuration) match {
case (_, Some(e)) => return e
case (s, _) => s
}
// if we detect -Dsbt.client=true or -client, run thin client.
val clientModByEnv = SysProp.client
val userCommands = configuration.arguments
@ -75,6 +71,12 @@ private[sbt] object xMain {
.filterNot(_ == DashDashServer)
val isClient: String => Boolean = cmd => (cmd == DashClient) || (cmd == DashDashClient)
val isBsp: String => Boolean = cmd => (cmd == "-bsp") || (cmd == "--bsp")
val isServer = !userCommands.exists(c => isBsp(c) || isClient(c))
val bootServerSocket = if (isServer) getSocketOrExit(configuration) match {
case (_, Some(e)) => return e
case (s, _) => s
}
else None
if (userCommands.exists(isBsp)) {
BspClient.run(dealiasBaseDirectory(configuration))
} else {

View File

@ -51,7 +51,6 @@ private[sbt] final class CommandExchange {
private var server: Option[ServerInstance] = None
private val firstInstance: AtomicBoolean = new AtomicBoolean(true)
private val monitoringActiveJson: AtomicBoolean = new AtomicBoolean(false)
private var consoleChannel: Option[ConsoleChannel] = None
private val commandQueue: LinkedBlockingQueue[Exec] = new LinkedBlockingQueue[Exec]
private val channelBuffer: ListBuffer[CommandChannel] = new ListBuffer()
private val channelBufferLock = new AnyRef {}
@ -60,6 +59,7 @@ private[sbt] final class CommandExchange {
private[this] val lastState = new AtomicReference[State]
private[this] val currentExecRef = new AtomicReference[Exec]
private[sbt] def hasServer = server.isDefined
addConsoleChannel()
def channels: List[CommandChannel] = channelBuffer.toList
@ -141,11 +141,9 @@ private[sbt] final class CommandExchange {
}
private def addConsoleChannel(): Unit =
if (consoleChannel.isEmpty) {
if (!Terminal.startedByRemoteClient) {
val name = ConsoleChannel.defaultName
val console0 = new ConsoleChannel(name, mkAskUser(name))
consoleChannel = Some(console0)
subscribe(console0)
subscribe(new ConsoleChannel(name, mkAskUser(name)))
}
def run(s: State): State = run(s, s.get(autoStartServer).getOrElse(true))
def run(s: State, autoStart: Boolean): State = {
@ -402,7 +400,6 @@ private[sbt] final class CommandExchange {
.withChannelName(currentExec.flatMap(_.source.map(_.channelName)))
case _ => pe
}
if (channels.isEmpty) addConsoleChannel()
channels.foreach(c => ProgressState.updateProgressState(newPE, c.terminal))
}