mirror of https://github.com/sbt/sbt.git
Use NetworkClient to implement `sbt -bsp`
Network client already supports the -bsp command (since
65ab7c94d0). This commit reworks the
BspClient.run method so that it delegates to the NetworkClient. The
advantage to doing it this way is that improvements to starting up the
sbt server by the thin client will automatically propagate to the -bsp
command. The way that it is implemented, all of the output generated
during server startup will be redirected to System.err which is useful
for debugging without messing up the bsp protocol, which relies on only
bsp messages being written to System.out.
This commit is contained in:
parent
68933a628d
commit
f3b3148c58
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue