Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process exit code on script errors #8169

Merged
merged 1 commit into from Jun 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 17 additions & 19 deletions src/compiler/scala/tools/nsc/ScriptRunner.scala
Expand Up @@ -82,7 +82,7 @@ abstract class AbstractScriptRunner(settings: GenericRunnerSettings) extends Scr
*
* @return true if compilation and the handler succeeds, false otherwise.
*/
private def withCompiledScript(scriptFile: String)(handler: String => Boolean): Boolean = {
private def withCompiledScript(scriptFile: String)(handler: String => Option[Throwable]): Option[Throwable] = {

/* Compiles the script file, and returns the directory with the compiled
* class files, if the compilation succeeded.
Expand All @@ -100,7 +100,7 @@ abstract class AbstractScriptRunner(settings: GenericRunnerSettings) extends Scr

def hasClassToRun(d: Directory): Boolean = DirectoryClassPath(d.jfile).findClass(mainClass).isDefined

def withLatestJar(): Boolean = {
def withLatestJar(): Option[Throwable] = {
/** Choose a jar filename to hold the compiled version of a script. */
def jarFileFor(scriptFile: String) = File(
if (scriptFile endsWith ".jar") scriptFile
Expand All @@ -109,15 +109,15 @@ abstract class AbstractScriptRunner(settings: GenericRunnerSettings) extends Scr
val jarFile = jarFileFor(scriptFile)
def jarOK = jarFile.canRead && (jarFile isFresher File(scriptFile))

def recompile() = {
def recompile(): Option[Throwable] = {
jarFile.delete()

compile match {
case Some(compiledPath) =>
if (!hasClassToRun(compiledPath)) {
// it compiled ok, but there is nothing to run;
// running an empty script should succeed
true
None
} else {
try io.Jar.create(jarFile, compiledPath, mainClass)
catch { case NonFatal(_) => jarFile.delete() }
Expand All @@ -129,7 +129,7 @@ abstract class AbstractScriptRunner(settings: GenericRunnerSettings) extends Scr
// jar failed; run directly from the class files
else handler(compiledPath.path)
}
case _ => false
case _ => Some(ScriptCompileError)
}
}

Expand All @@ -143,20 +143,22 @@ abstract class AbstractScriptRunner(settings: GenericRunnerSettings) extends Scr
util.waitingForThreads {
// either update the jar or don't use a cache jar at all, just use the class files, if they exist
if (settings.save) withLatestJar()
else compile.exists(cp => !hasClassToRun(cp) || handler(cp.path))
else {
compile match {
case Some(cp) => if (hasClassToRun(cp)) handler(cp.path) else None
case _ => Some(ScriptCompileError)
}
}
}
}

/** Run a script after it has been compiled. Prints any exceptions.
*
* @return true if execution succeeded, false otherwise
*/
private def runCompiled(compiledLocation: String, scriptArgs: List[String]): Boolean = {
private def runCompiled(compiledLocation: String, scriptArgs: List[String]): Option[Throwable] = {
val cp = File(compiledLocation).toURL +: settings.classpathURLs
ObjectRunner.runAndCatch(cp, mainClass, scriptArgs) match {
case Some(e) => e.printStackTrace() ; false
case _ => true
}
ObjectRunner.runAndCatch(cp, mainClass, scriptArgs)
}

final def runScript(scriptFile: String, scriptArgs: List[String]): Option[Throwable] = {
Expand All @@ -165,21 +167,15 @@ abstract class AbstractScriptRunner(settings: GenericRunnerSettings) extends Scr
else if (!f.canRead) Some(new IOException(s"can't read: $scriptFile"))
else if (f.isDirectory) Some(new IOException(s"can't compile a directory: $scriptFile"))
else if (!settings.nc && !f.isFile) Some(new IOException(s"compile server requires a regular file: $scriptFile"))
else {
withCompiledScript(scriptFile) { runCompiled(_, scriptArgs) }
None
}
else withCompiledScript(scriptFile) { runCompiled(_, scriptArgs) }
}

final def runScriptText(command: String, scriptArgs: List[String]): Option[Throwable] = {
val scriptFile = File.makeTemp("scalacmd", ".scala")
// save the command to the file
scriptFile writeAll command

try {
withCompiledScript(scriptFile.path) { runCompiled(_, scriptArgs) }
None
}
try withCompiledScript(scriptFile.path) { runCompiled(_, scriptArgs) }
catch {
case NonFatal(e) => Some(e)
}
Expand Down Expand Up @@ -209,3 +205,5 @@ object ScriptRunner {
loader.create[ScriptRunner](custom, settings.errorFn)(settings)
}
}

object ScriptCompileError extends scala.util.control.ControlThrowable
3 changes: 2 additions & 1 deletion src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala
Expand Up @@ -89,7 +89,8 @@ class MainGenericRunner {
}

runTarget() match {
case e @ Some(ex) => errorFn("", e) // there must be a useful message of hope to offer here
case Some(ScriptCompileError) => false
case e @ Some(ex) => errorFn("", e)
case _ => true
}
}
Expand Down