mirror of https://github.com/sbt/sbt.git
[1.x] Use JProcess for interactive forking (#8678)
**Problem/Solution** For forking that require both input and output, use a Java process instead of the Scala process.
This commit is contained in:
parent
6e974bb283
commit
2380082bcc
|
|
@ -180,7 +180,8 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[_]] = Def settings
|
|||
exclude[DirectMissingMethodProblem]("sbt.PluginData.this"),
|
||||
exclude[IncompatibleResultTypeProblem]("sbt.EvaluateTask.executeProgress"),
|
||||
exclude[DirectMissingMethodProblem]("sbt.Keys.currentTaskProgress"),
|
||||
exclude[IncompatibleResultTypeProblem]("sbt.PluginData.copy$default$10")
|
||||
exclude[IncompatibleResultTypeProblem]("sbt.PluginData.copy$default$10"),
|
||||
exclude[IncompatibleMethTypeProblem]("sbt.internal.util.*"),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package sbt.internal.util
|
||||
|
||||
import java.lang.{ Process => JProcess }
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import scala.sys.process.Process
|
||||
|
||||
|
|
@ -18,17 +19,21 @@ import scala.sys.process.Process
|
|||
* processes when the user inputs ctrl+c.
|
||||
*/
|
||||
private[sbt] object RunningProcesses {
|
||||
val active = ConcurrentHashMap.newKeySet[Process]
|
||||
def add(process: Process): Unit = active.synchronized {
|
||||
val active = ConcurrentHashMap.newKeySet[AnyRef]
|
||||
def add(process: AnyRef): Unit = active.synchronized {
|
||||
active.add(process)
|
||||
()
|
||||
}
|
||||
def remove(process: Process): Unit = active.synchronized {
|
||||
def remove(process: AnyRef): Unit = active.synchronized {
|
||||
active.remove(process)
|
||||
()
|
||||
}
|
||||
def killAll(): Unit = active.synchronized {
|
||||
active.forEach(_.destroy())
|
||||
active.forEach {
|
||||
case p: Process => p.destroy()
|
||||
case p: JProcess => p.destroy()
|
||||
case _ => ()
|
||||
}
|
||||
active.clear()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import OutputStrategy._
|
|||
import sbt.internal.util.{ RunningProcesses, Util }
|
||||
import Util.{ AnyOps, none }
|
||||
|
||||
import java.lang.{ ProcessBuilder => JProcessBuilder }
|
||||
import java.lang.{ ProcessBuilder => JProcessBuilder, Process => JProcess }
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
|
|
@ -33,8 +33,18 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
|
|||
* It is configured according to `config`.
|
||||
* If `runnerClass` is defined for this Fork instance, it is prepended to `arguments` to define the arguments passed to the forked command.
|
||||
*/
|
||||
def apply(config: ForkOptions, arguments: Seq[String]): Int =
|
||||
Fork.blockForExitCode(fork(config, arguments))
|
||||
def apply(config: ForkOptions, arguments: Seq[String]): Int = {
|
||||
import config._
|
||||
val outStrategy = outputStrategy.getOrElse(StdoutOutput)
|
||||
if (connectInput && outStrategy == StdoutOutput)
|
||||
Fork.blockJForExitCode(interactiveFork(config, arguments))
|
||||
else Fork.blockForExitCode(fork(config, arguments))
|
||||
}
|
||||
|
||||
private def interactiveFork(config: ForkOptions, arguments: Seq[String]): JProcess = {
|
||||
val (extraEnv, jpb) = prep(config, arguments)
|
||||
Fork.forkInternalInteractive(config, extraEnv, jpb)
|
||||
}
|
||||
|
||||
/**
|
||||
* Forks the configured process and returns a `Process` that can be used to wait for completion or to terminate the forked process.
|
||||
|
|
@ -43,6 +53,14 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
|
|||
* If `runnerClass` is defined for this Fork instance, it is prepended to `arguments` to define the arguments passed to the forked command.
|
||||
*/
|
||||
def fork(config: ForkOptions, arguments: Seq[String]): Process = {
|
||||
val (extraEnv, jpb) = prep(config, arguments)
|
||||
Fork.forkInternal(config, extraEnv, jpb)
|
||||
}
|
||||
|
||||
private def prep(
|
||||
config: ForkOptions,
|
||||
arguments: Seq[String]
|
||||
): (List[(String, String)], JProcessBuilder) = {
|
||||
import config._
|
||||
val executable = Fork.javaCommand(javaHome, commandName).getAbsolutePath
|
||||
val preOptions = makeOptions(runJVMOptions, bootJars, arguments)
|
||||
|
|
@ -58,7 +76,7 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
|
|||
val extraEnv = classpathEnv.toList.map { value =>
|
||||
Fork.ClasspathEnvKey -> value
|
||||
}
|
||||
Fork.forkInternal(config, extraEnv, jpb)
|
||||
(extraEnv, jpb)
|
||||
}
|
||||
|
||||
private[this] def makeOptions(
|
||||
|
|
@ -187,6 +205,30 @@ object Fork {
|
|||
}
|
||||
}
|
||||
|
||||
private[sbt] def forkInternalInteractive(
|
||||
config: ForkOptions,
|
||||
extraEnv: List[(String, String)],
|
||||
jpb: JProcessBuilder
|
||||
): JProcess = {
|
||||
import config.{ envVars => env, _ }
|
||||
val environment: List[(String, String)] = env.toList ++ extraEnv
|
||||
workingDirectory.foreach(jpb.directory(_))
|
||||
environment.foreach { case (k, v) => jpb.environment.put(k, v) }
|
||||
jpb.inheritIO()
|
||||
jpb.start()
|
||||
}
|
||||
|
||||
private[sbt] def blockJForExitCode(p: JProcess): Int = {
|
||||
RunningProcesses.add(p)
|
||||
try {
|
||||
p.waitFor()
|
||||
p.exitValue()
|
||||
} finally {
|
||||
if (p.isAlive()) p.destroy()
|
||||
RunningProcesses.remove(p)
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] def blockForExitCode(p: Process): Int = {
|
||||
RunningProcesses.add(p)
|
||||
try p.exitValue()
|
||||
|
|
|
|||
Loading…
Reference in New Issue