mirror of https://github.com/sbt/sbt.git
Fork server if it's not running
Fixes https://github.com/sbt/sbt/issues/3508 This forks an instance of sbt in the background when it's not running already. ``` $ time sbt -client compile Getting org.scala-sbt sbt 1.2.0-SNAPSHOT (this may take some time)... :: retrieving :: org.scala-sbt#boot-app confs: [default] 79 artifacts copied, 0 already retrieved (28214kB/130ms) [info] entering *experimental* thin client - BEEP WHIRR [info] server was not detected. starting an instance [info] waiting for the server... [info] waiting for the server... [info] server found > compile [success] completed sbt -client compile 9.25s user 2.39s system 33% cpu 34.893 total $ time sbt -client compile [info] entering *experimental* thin client - BEEP WHIRR > compile [success] completed sbt -client compile 3.55s user 1.68s system 107% cpu 4.889 total ```
This commit is contained in:
parent
3eb76125e0
commit
f3038167a5
|
|
@ -297,7 +297,7 @@ object BasicCommands {
|
||||||
case e :: Nil if e.commandLine == "shell" => Nil
|
case e :: Nil if e.commandLine == "shell" => Nil
|
||||||
case xs => xs map (_.commandLine)
|
case xs => xs map (_.commandLine)
|
||||||
})
|
})
|
||||||
NetworkClient.run(s0.configuration.baseDirectory, arguments)
|
NetworkClient.run(s0.configuration, arguments)
|
||||||
"exit" :: s0.copy(remainingCommands = Nil)
|
"exit" :: s0.copy(remainingCommands = Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,13 @@ package sbt
|
||||||
package internal
|
package internal
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.{ File, IOException }
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import scala.util.{ Success, Failure }
|
import scala.util.{ Success, Failure }
|
||||||
|
import scala.sys.process.{ BasicIO, Process, ProcessLogger }
|
||||||
import sbt.protocol._
|
import sbt.protocol._
|
||||||
import sbt.internal.protocol._
|
import sbt.internal.protocol._
|
||||||
import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams }
|
import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams }
|
||||||
|
|
@ -24,7 +25,7 @@ import sbt.io.syntax._
|
||||||
import sbt.io.IO
|
import sbt.io.IO
|
||||||
import sjsonnew.support.scalajson.unsafe.Converter
|
import sjsonnew.support.scalajson.unsafe.Converter
|
||||||
|
|
||||||
class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
class NetworkClient(configuration: xsbti.AppConfiguration, arguments: List[String]) { self =>
|
||||||
private val channelName = new AtomicReference("_")
|
private val channelName = new AtomicReference("_")
|
||||||
private val status = new AtomicReference("Ready")
|
private val status = new AtomicReference("Ready")
|
||||||
private val lock: AnyRef = new AnyRef {}
|
private val lock: AnyRef = new AnyRef {}
|
||||||
|
|
@ -32,6 +33,7 @@ class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
||||||
private val pendingExecIds = ListBuffer.empty[String]
|
private val pendingExecIds = ListBuffer.empty[String]
|
||||||
|
|
||||||
private val console = ConsoleAppender("thin1")
|
private val console = ConsoleAppender("thin1")
|
||||||
|
private def baseDirectory: File = configuration.baseDirectory
|
||||||
|
|
||||||
lazy val connection = init()
|
lazy val connection = init()
|
||||||
|
|
||||||
|
|
@ -40,7 +42,9 @@ class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
||||||
// Open server connection based on the portfile
|
// Open server connection based on the portfile
|
||||||
def init(): ServerConnection = {
|
def init(): ServerConnection = {
|
||||||
val portfile = baseDirectory / "project" / "target" / "active.json"
|
val portfile = baseDirectory / "project" / "target" / "active.json"
|
||||||
if (!portfile.exists) sys.error("server does not seem to be running.")
|
if (!portfile.exists) {
|
||||||
|
forkServer(portfile)
|
||||||
|
}
|
||||||
val (sk, tkn) = ClientSocket.socket(portfile)
|
val (sk, tkn) = ClientSocket.socket(portfile)
|
||||||
val conn = new ServerConnection(sk) {
|
val conn = new ServerConnection(sk) {
|
||||||
override def onNotification(msg: JsonRpcNotificationMessage): Unit = self.onNotification(msg)
|
override def onNotification(msg: JsonRpcNotificationMessage): Unit = self.onNotification(msg)
|
||||||
|
|
@ -57,6 +61,42 @@ class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
||||||
conn
|
conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forks another instance of sbt in the background.
|
||||||
|
* This instance must be shutdown explicitly via `sbt -client shutdown`
|
||||||
|
*/
|
||||||
|
def forkServer(portfile: File): Unit = {
|
||||||
|
console.appendLog(Level.Info, "server was not detected. starting an instance")
|
||||||
|
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)
|
||||||
|
.toList
|
||||||
|
.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 cmd = "sbt"
|
||||||
|
val io = BasicIO(false, ProcessLogger(_ => ()))
|
||||||
|
val _ = Process(cmd, baseDirectory).run(io)
|
||||||
|
def waitForPortfile(n: Int): Unit =
|
||||||
|
if (portfile.exists) {
|
||||||
|
console.appendLog(Level.Info, "server found")
|
||||||
|
} else {
|
||||||
|
if (n <= 0) sys.error(s"timeout. $portfile is not found.")
|
||||||
|
else {
|
||||||
|
Thread.sleep(1000)
|
||||||
|
if ((n - 1) % 10 == 0) {
|
||||||
|
console.appendLog(Level.Info, "waiting for the server...")
|
||||||
|
}
|
||||||
|
waitForPortfile(n - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
waitForPortfile(90)
|
||||||
|
}
|
||||||
|
|
||||||
/** Called on the response for a returning message. */
|
/** Called on the response for a returning message. */
|
||||||
def onReturningReponse(msg: JsonRpcResponseMessage): Unit = {
|
def onReturningReponse(msg: JsonRpcResponseMessage): Unit = {
|
||||||
def printResponse(): Unit = {
|
def printResponse(): Unit = {
|
||||||
|
|
@ -152,6 +192,7 @@ class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
||||||
|
|
||||||
def start(): Unit = {
|
def start(): Unit = {
|
||||||
console.appendLog(Level.Info, "entering *experimental* thin client - BEEP WHIRR")
|
console.appendLog(Level.Info, "entering *experimental* thin client - BEEP WHIRR")
|
||||||
|
val _ = connection
|
||||||
val userCommands = arguments filterNot { cmd =>
|
val userCommands = arguments filterNot { cmd =>
|
||||||
cmd.startsWith("-")
|
cmd.startsWith("-")
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +203,9 @@ class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
||||||
def batchExecute(userCommands: List[String]): Unit = {
|
def batchExecute(userCommands: List[String]): Unit = {
|
||||||
userCommands foreach { cmd =>
|
userCommands foreach { cmd =>
|
||||||
println("> " + cmd)
|
println("> " + cmd)
|
||||||
val execId = sendExecCommand(cmd)
|
val execId =
|
||||||
|
if (cmd == "shutdown") sendExecCommand("exit")
|
||||||
|
else sendExecCommand(cmd)
|
||||||
while (pendingExecIds contains execId) {
|
while (pendingExecIds contains execId) {
|
||||||
Thread.sleep(100)
|
Thread.sleep(100)
|
||||||
}
|
}
|
||||||
|
|
@ -176,6 +219,8 @@ class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
||||||
case Some("shutdown") =>
|
case Some("shutdown") =>
|
||||||
// `sbt -client shutdown` shuts down the server
|
// `sbt -client shutdown` shuts down the server
|
||||||
sendExecCommand("exit")
|
sendExecCommand("exit")
|
||||||
|
Thread.sleep(100)
|
||||||
|
running.set(false)
|
||||||
case Some("exit") =>
|
case Some("exit") =>
|
||||||
running.set(false)
|
running.set(false)
|
||||||
case Some(s) if s.trim.nonEmpty =>
|
case Some(s) if s.trim.nonEmpty =>
|
||||||
|
|
@ -213,9 +258,9 @@ class NetworkClient(baseDirectory: File, arguments: List[String]) { self =>
|
||||||
}
|
}
|
||||||
|
|
||||||
object NetworkClient {
|
object NetworkClient {
|
||||||
def run(baseDirectory: File, arguments: List[String]): Unit =
|
def run(configuration: xsbti.AppConfiguration, arguments: List[String]): Unit =
|
||||||
try {
|
try {
|
||||||
new NetworkClient(baseDirectory, arguments)
|
new NetworkClient(configuration, arguments)
|
||||||
()
|
()
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => println(e.getMessage)
|
case NonFatal(e) => println(e.getMessage)
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ final class xMain extends xsbti.AppMain {
|
||||||
val args = userCommands.toList filterNot { cmd =>
|
val args = userCommands.toList filterNot { cmd =>
|
||||||
(cmd == DashClient) || (cmd == DashDashClient)
|
(cmd == DashClient) || (cmd == DashDashClient)
|
||||||
}
|
}
|
||||||
NetworkClient.run(configuration.baseDirectory, args)
|
NetworkClient.run(configuration, args)
|
||||||
Exit(0)
|
Exit(0)
|
||||||
} else {
|
} else {
|
||||||
val state = StandardMain.initialState(
|
val state = StandardMain.initialState(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue