From 9519eb094130ab121fa10767916e812b76bdc947 Mon Sep 17 00:00:00 2001 From: Antoine Gourlay Date: Tue, 26 Aug 2014 09:43:49 +0200 Subject: SI-5254 running an empty scala script should succeed The script runner made the assumption that "compilation succeeded" implies "there is a Main class to run", but this can be wrong if the script is empty (or only contains imports/comments). The ScriptRunner now uses the ClassPath utility to check if there really is a main class. If not, it doesn't try to run it and returns peacefully. This also makes `scala -e ''` succeed. --- src/compiler/scala/tools/nsc/ScriptRunner.scala | 34 +++++++++++++++-------- test/junit/scala/tools/nsc/ScriptRunnerTest.scala | 23 +++++++++++++++ 2 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 test/junit/scala/tools/nsc/ScriptRunnerTest.scala diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index c2d62db558..7d5c6f6fff 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -6,7 +6,7 @@ package scala package tools.nsc -import io.{ Directory, File, Path } +import io.{ AbstractFile, Directory, File, Path } import java.io.IOException import scala.tools.nsc.reporters.{Reporter,ConsoleReporter} import util.Exceptional.unwrap @@ -111,6 +111,12 @@ class ScriptRunner extends HasCompileSocket { else None } + def hasClassToRun(d: Directory): Boolean = { + import util.ClassPath.{ DefaultJavaContext => ctx } + val cp = ctx.newClassPath(AbstractFile.getDirectory(d)) + cp.findClass(mainClass).isDefined + } + /* The script runner calls sys.exit to communicate a return value, but this must * not take place until there are no non-daemon threads running. Tickets #1955, #2006. */ @@ -124,15 +130,21 @@ class ScriptRunner extends HasCompileSocket { compile match { case Some(compiledPath) => - try io.Jar.create(jarFile, compiledPath, mainClass) - catch { case _: Exception => jarFile.delete() } - - if (jarOK) { - compiledPath.deleteRecursively() - handler(jarFile.toAbsolute.path) + if (!hasClassToRun(compiledPath)) { + // it compiled ok, but there is nothing to run; + // running an empty script should succeed + true + } else { + try io.Jar.create(jarFile, compiledPath, mainClass) + catch { case _: Exception => jarFile.delete() } + + if (jarOK) { + compiledPath.deleteRecursively() + handler(jarFile.toAbsolute.path) + } + // jar failed; run directly from the class files + else handler(compiledPath.path) } - // jar failed; run directly from the class files - else handler(compiledPath.path) case _ => false } } @@ -140,8 +152,8 @@ class ScriptRunner extends HasCompileSocket { if (jarOK) handler(jarFile.toAbsolute.path) // pre-compiled jar is current else recompile() // jar old - recompile the script. } - // don't use a cache jar at all--just use the class files - else compile exists (cp => handler(cp.path)) + // don't use a cache jar at all--just use the class files, if they exist + else compile exists (cp => !hasClassToRun(cp) || handler(cp.path)) } } diff --git a/test/junit/scala/tools/nsc/ScriptRunnerTest.scala b/test/junit/scala/tools/nsc/ScriptRunnerTest.scala new file mode 100644 index 0000000000..9bae7a0487 --- /dev/null +++ b/test/junit/scala/tools/nsc/ScriptRunnerTest.scala @@ -0,0 +1,23 @@ +package scala.tools.nsc + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class ScriptRunnerTest { + @Test + def testEmptyScriptSucceeds: Unit = { + val s = new GenericRunnerSettings(s => ()) + s.nc.value = true + s.usejavacp.value = true + + // scala -nc -e '' + assertTrue(ScriptRunner.runCommand(s, "", Nil)) + + // scala -nc -save -e '' + s.save.value = true + assertTrue(ScriptRunner.runCommand(s, "", Nil)) + } +} -- cgit v1.2.3