Merge pull request #6739 from adpi2/fix-cancel

Fix #6738: register all forked process
This commit is contained in:
eugene yokota 2021-12-05 12:38:17 -05:00 committed by GitHub
commit f70ef045b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 26 deletions

View File

@ -18,7 +18,7 @@ import sbt.util.Logger
import sbt.ConcurrentRestrictions.Tag import sbt.ConcurrentRestrictions.Tag
import sbt.protocol.testing._ import sbt.protocol.testing._
import sbt.internal.util.Util.{ AnyOps, none } import sbt.internal.util.Util.{ AnyOps, none }
import sbt.internal.util.{ RunningProcesses, Terminal => UTerminal } import sbt.internal.util.{ Terminal => UTerminal }
private[sbt] object ForkTests { private[sbt] object ForkTests {
def apply( def apply(
@ -158,13 +158,7 @@ private[sbt] object ForkTests {
classOf[ForkMain].getCanonicalName, classOf[ForkMain].getCanonicalName,
server.getLocalPort.toString server.getLocalPort.toString
) )
val p = Fork.java.fork(fork, options) val ec = Fork.java(fork, options)
RunningProcesses.add(p)
val ec = try p.exitValue()
finally {
if (p.isAlive()) p.destroy()
RunningProcesses.remove(p)
}
val result = val result =
if (ec != 0) if (ec != 0)
TestOutput( TestOutput(

View File

@ -9,10 +9,9 @@ package sbt
import java.io.File import java.io.File
import java.lang.ProcessBuilder.Redirect import java.lang.ProcessBuilder.Redirect
import scala.sys.process.Process import scala.sys.process.Process
import OutputStrategy._ import OutputStrategy._
import sbt.internal.util.Util import sbt.internal.util.{ RunningProcesses, Util }
import Util.{ AnyOps, none } import Util.{ AnyOps, none }
import java.lang.{ ProcessBuilder => JProcessBuilder } import java.lang.{ ProcessBuilder => JProcessBuilder }
@ -31,7 +30,15 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
* It is configured according to `config`. * 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. * 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(config, arguments).exitValue() def apply(config: ForkOptions, arguments: Seq[String]): Int = {
val p = fork(config, arguments)
RunningProcesses.add(p)
try p.exitValue()
finally {
if (p.isAlive()) p.destroy()
RunningProcesses.remove(p)
}
}
/** /**
* Forks the configured process and returns a `Process` that can be used to wait for completion or to terminate the forked process. * Forks the configured process and returns a `Process` that can be used to wait for completion or to terminate the forked process.

View File

@ -10,7 +10,6 @@ package sbt
import java.io.File import java.io.File
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Modifier.{ isPublic, isStatic } import java.lang.reflect.Modifier.{ isPublic, isStatic }
import sbt.internal.inc.ScalaInstance import sbt.internal.inc.ScalaInstance
import sbt.internal.inc.classpath.{ ClasspathFilter, ClasspathUtil } import sbt.internal.inc.classpath.{ ClasspathFilter, ClasspathUtil }
import sbt.internal.util.MessageOnlyException import sbt.internal.util.MessageOnlyException
@ -34,29 +33,40 @@ class ForkRun(config: ForkOptions) extends ScalaRun {
s"""Nonzero exit code returned from $label: $exitCode""".stripMargin s"""Nonzero exit code returned from $label: $exitCode""".stripMargin
) )
) )
val process = fork(mainClass, classpath, options, log)
def cancel() = { log.info(s"running (fork) $mainClass ${Run.runOptionsStr(options)}")
log.warn("Run canceled.") val c = configLogged(log)
process.destroy() val scalaOpts = scalaOptions(mainClass, classpath, options)
1 val exitCode = try Fork.java(c, scalaOpts)
catch {
case _: InterruptedException =>
log.warn("Run canceled.")
1
} }
val exitCode = try process.exitValue()
catch { case _: InterruptedException => cancel() }
processExitCode(exitCode, "runner") processExitCode(exitCode, "runner")
} }
def fork(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Process = { def fork(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Process = {
log.info(s"running (fork) $mainClass ${Run.runOptionsStr(options)}") log.info(s"running (fork) $mainClass ${Run.runOptionsStr(options)}")
val scalaOptions = classpathOption(classpath) ::: mainClass :: options.toList val c = configLogged(log)
val configLogged = val scalaOpts = scalaOptions(mainClass, classpath, options)
if (config.outputStrategy.isDefined) config
else config.withOutputStrategy(OutputStrategy.LoggedOutput(log))
// fork with Java because Scala introduces an extra class loader (#702) // fork with Java because Scala introduces an extra class loader (#702)
Fork.java.fork(configLogged, scalaOptions) Fork.java.fork(c, scalaOpts)
} }
private def classpathOption(classpath: Seq[File]) =
"-classpath" :: Path.makeString(classpath) :: Nil private def configLogged(log: Logger): ForkOptions = {
if (config.outputStrategy.isDefined) config
else config.withOutputStrategy(OutputStrategy.LoggedOutput(log))
}
private def scalaOptions(
mainClass: String,
classpath: Seq[File],
options: Seq[String]
): Seq[String] =
"-classpath" :: Path.makeString(classpath) :: mainClass :: options.toList
} }
class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolean) class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolean)