Skip to content

Commit

Permalink
Drop TrapExit
Browse files Browse the repository at this point in the history
Fixes #6558

Problem
-------
sbt uses SecurityManager feature of JDK to trap `sys.exit` call during
`run`-like tasks, since we emulate `run` and `console` as function calls.
JDK 17 deprecated SecurityManager and it's printing warnings.

Solution
--------
About 10 years go, `exit` was a convenient way of quitting both Scala
REPL and sbt shell. Scala 2.11 broke this by removing the `Predef.exit`.
We still need to worry about `run` potentially calling `sys.exit`
but that can be handled using fork feature.
In the long-run, it probably is better to be JDK 17 compatible.
  • Loading branch information
eed3si9n committed Sep 19, 2021
1 parent 45d6e13 commit cd0d94e
Show file tree
Hide file tree
Showing 9 changed files with 41 additions and 536 deletions.
3 changes: 3 additions & 0 deletions build.sbt
Expand Up @@ -598,6 +598,9 @@ lazy val runProj = (project in file("run"))
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#CustomOutput.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#LoggedOutput.copy"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#LoggedOutput.copy$default$*"),
exclude[Problem]("sbt.TrapExit*"),
exclude[MissingClassProblem]("sbt.ExitCode"), // private
exclude[MissingClassProblem]("sbt.LoggingExceptionHandler"), // private
)
)
.configure(addSbtIO, addSbtCompilerClasspath)
Expand Down
2 changes: 1 addition & 1 deletion main-actions/src/main/scala/sbt/Console.scala
Expand Up @@ -71,7 +71,7 @@ final class Console(compiler: AnalyzingCompiler) {
terminal.withRawOutput {
jline.TerminalFactory.set(terminal.toJLine)
DeprecatedJLine.setTerminalOverride(jline3term)
terminal.withRawInput(Run.executeTrapExit(console0, log))
terminal.withRawInput(Run.executeSuccess(console0))
}
} finally {
sys.props("scala.color") = previous
Expand Down
2 changes: 1 addition & 1 deletion main/src/main/scala/sbt/Keys.scala
Expand Up @@ -281,7 +281,7 @@ object Keys {
val runMain = inputKey[Unit]("Runs the main class selected by the first argument, passing the remaining arguments to the main method.").withRank(ATask)
val discoveredMainClasses = taskKey[Seq[String]]("Auto-detects main classes.").withRank(BMinusTask)
val runner = taskKey[ScalaRun]("Implementation used to run a main class.").withRank(DTask)
val trapExit = settingKey[Boolean]("If true, enables exit trapping and thread management for 'run'-like tasks. This is currently only suitable for serially-executed 'run'-like tasks.").withRank(CSetting)
val trapExit = settingKey[Boolean]("If true, enables exit trapping and thread management for 'run'-like tasks. This was removed in sbt 1.6.0 due to JDK 17 deprecating Security Manager.").withRank(CSetting)

val fork = settingKey[Boolean]("If true, forks a new JVM when running. If false, runs in the same JVM as the build.").withRank(ASetting)
val forkOptions = taskKey[ForkOptions]("Configures JVM forking.").withRank(DSetting)
Expand Down
21 changes: 8 additions & 13 deletions main/src/main/scala/sbt/Main.scala
Expand Up @@ -210,21 +210,16 @@ object StandardMain {

private[this] val isShutdown = new AtomicBoolean(false)
def runManaged(s: State): xsbti.MainResult = {
val previous = TrapExit.installManager()
val hook = ShutdownHooks.add(closeRunnable)
try {
val hook = ShutdownHooks.add(closeRunnable)
try {
MainLoop.runLogged(s)
} catch {
case _: InterruptedException if isShutdown.get =>
new xsbti.Exit { override def code(): Int = 0 }
} finally {
try DefaultBackgroundJobService.shutdown()
finally hook.close()
()
}
MainLoop.runLogged(s)
} catch {
case _: InterruptedException if isShutdown.get =>
new xsbti.Exit { override def code(): Int = 0 }
} finally {
TrapExit.uninstallManager(previous)
try DefaultBackgroundJobService.shutdown()
finally hook.close()
()
}
}

Expand Down
16 changes: 8 additions & 8 deletions run/src/main/scala/sbt/Run.scala
Expand Up @@ -58,6 +58,7 @@ class ForkRun(config: ForkOptions) extends ScalaRun {
private def classpathOption(classpath: Seq[File]) =
"-classpath" :: Path.makeString(classpath) :: Nil
}

class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolean)
extends ScalaRun {
def this(instance: ScalaInstance, trapExit: Boolean, nativeTmp: File) =
Expand Down Expand Up @@ -105,9 +106,8 @@ class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolea
// log.trace(e)
throw e
}
// try { execute(); None } catch { case e: Exception => log.trace(e); Some(e.toString) }

if (trapExit) Run.executeTrapExit(execute(), log)
if (trapExit) Run.executeSuccess(execute())
else directExecute()
}

Expand Down Expand Up @@ -169,12 +169,12 @@ object Run {
runner.run(mainClass, classpath, options, log)

/** Executes the given function, trapping calls to System.exit. */
def executeTrapExit(f: => Unit, log: Logger): Try[Unit] = {
val exitCode = TrapExit(f, log)
if (exitCode == 0) {
log.debug("Exited with code 0")
Success(())
} else Failure(new MessageOnlyException("Nonzero exit code: " + exitCode))
@deprecated("TrapExit feature is removed; just call the function instead", "1.6.0")
def executeTrapExit(f: => Unit, log: Logger): Try[Unit] = executeSuccess(f)

private[sbt] def executeSuccess(f: => Unit): Try[Unit] = {
f
Success(())
}

// quotes the option that includes a whitespace
Expand Down

0 comments on commit cd0d94e

Please sign in to comment.