diff --git a/main-command/src/main/scala/sbt/internal/client/BspClient.scala b/main-command/src/main/scala/sbt/internal/client/BspClient.scala index c9942ba99..19b8e106a 100644 --- a/main-command/src/main/scala/sbt/internal/client/BspClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/BspClient.scala @@ -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)) } } diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index c89715661..ccf33bbe4 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -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 } diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index d7b857fa4..1f5888c78 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -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 { diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 29f34f7da..3569287bb 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -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)) }