diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala index b1aa51318..bd0fb7774 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala @@ -538,6 +538,7 @@ object Terminal { case _ => None } } + private[sbt] def startedByRemoteClient = props.isDefined /** * Creates an instance of [[Terminal]] that delegates most of its methods to an underlying diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index a2c85eb6e..58f2e20cd 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -1019,22 +1019,31 @@ object BuiltinCommands { val exchange = StandardMain.exchange val welcomeState = displayWelcomeBanner(s0) val s1 = exchange run welcomeState - exchange prompt ConsolePromptEvent(s0) - val minGCInterval = Project - .extract(s1) - .getOpt(Keys.minForcegcInterval) - .getOrElse(GCUtil.defaultMinForcegcInterval) - val exec: Exec = getExec(s1, minGCInterval) - val newState = s1 - .copy( - onFailure = Some(Exec(Shell, None)), - remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands - ) - .setInteractive(true) - val res = - if (exec.commandLine.trim.isEmpty) newState - else newState.clearGlobalLog - res + /* + * It is possible for sbt processes to leak if two are started simultaneously + * by a remote client and only one is able to start a server. This seems to + * happen primarily on windows. + */ + if (Terminal.startedByRemoteClient && !exchange.hasServer) { + Exec("shutdown", None) +: s1 + } else { + exchange prompt ConsolePromptEvent(s0) + val minGCInterval = Project + .extract(s1) + .getOpt(Keys.minForcegcInterval) + .getOrElse(GCUtil.defaultMinForcegcInterval) + val exec: Exec = getExec(s1, minGCInterval) + val newState = s1 + .copy( + onFailure = Some(Exec(Shell, None)), + remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands + ) + .setInteractive(true) + val res = + if (exec.commandLine.trim.isEmpty) newState + else newState.clearGlobalLog + res + } } def startServer: Command = diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index aa35b0cf6..25f74d7e1 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -53,6 +53,7 @@ private[sbt] final class CommandExchange { private val nextChannelId: AtomicInteger = new AtomicInteger(0) private[this] val lastState = new AtomicReference[State] private[this] val currentExecRef = new AtomicReference[Exec] + private[sbt] def hasServer = server.isDefined def channels: List[CommandChannel] = channelBuffer.toList