add BspClient

This commit is contained in:
Adrien Piquerez 2020-05-14 16:22:33 +02:00
parent 2c8a322bd8
commit c80fe525c6
2 changed files with 96 additions and 12 deletions

View File

@ -0,0 +1,78 @@
package sbt.internal.client
import java.io.{ File, InputStream, OutputStream }
import java.net.Socket
import sbt.Exit
import sbt.io.syntax._
import sbt.protocol.ClientSocket
import scala.sys.process.Process
import scala.util.control.NonFatal
class BspClient private (sbtServer: Socket) {
private def transferTo(input: InputStream, output: OutputStream): Unit = {
val buffer = Array.ofDim[Byte](1024)
while (true) {
val size = input.read(buffer)
output.write(buffer, 0, size)
output.flush()
}
}
private def run(): Exit = {
try {
val redirection = new Thread {
override def run(): Unit = transferTo(sbtServer.getInputStream, System.out)
}
redirection.start()
transferTo(System.in, sbtServer.getOutputStream)
Exit(0)
} catch {
case NonFatal(_) => Exit(1)
}
}
}
object BspClient {
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[String]()
val launchOpts = List("-Xms2048M", "-Xmx2048M", "-Xss2M")
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 process = Process(cmd, baseDirectory).run()
while (process.isAlive() && !portfile.exists) Thread.sleep(100)
if (!process.isAlive()) sys.error("sbt server exited")
}
}

View File

@ -19,6 +19,7 @@ import sbt.compiler.EvalImports
import sbt.internal.Aggregation.AnyKeys
import sbt.internal.CommandStrings.BootCommand
import sbt.internal._
import sbt.internal.client.BspClient
import sbt.internal.inc.ScalaInstance
import sbt.internal.nio.CheckBuildSources
import sbt.internal.util.Types.{ const, idFun }
@ -51,18 +52,23 @@ private[sbt] object xMain {
val clientModByEnv = SysProp.client
val userCommands = configuration.arguments.map(_.trim)
val isClient: String => Boolean = cmd => (cmd == DashClient) || (cmd == DashDashClient)
Terminal.withStreams {
if (clientModByEnv || userCommands.exists(isClient)) {
val args = userCommands.toList.filterNot(isClient)
NetworkClient.run(configuration, args)
Exit(0)
} else {
val state = StandardMain.initialState(
configuration,
Seq(defaults, early),
runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil
)
StandardMain.runManaged(state)
val isBsp: String => Boolean = cmd => (cmd == "-bsp") || (cmd == "--bsp")
if (userCommands.exists(isBsp)) {
BspClient.run(configuration)
} else {
Terminal.withStreams {
if (clientModByEnv || userCommands.exists(isClient)) {
val args = userCommands.toList.filterNot(isClient)
NetworkClient.run(configuration, args)
Exit(0)
} else {
val state = StandardMain.initialState(
configuration,
Seq(defaults, early),
runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil
)
StandardMain.runManaged(state)
}
}
}
} finally {