javascala

Does `scala` not need -jar, while `java` does, when executing a .jar file?


I was wondering why scala doesn't need -jar, while java does, when executing a .jar file?

For example:

scala target/some.jar

java -jar target/someother.jar

I look at the scala shell script, it invokes java as:

execCommand \
  "${JAVACMD:=java}" \
  $JAVA_OPTS \
  "${java_args[@]}" \
  "${classpath_args[@]}" \
  -Dscala.home="$SCALA_HOME" \
  $OVERRIDE_USEJAVACP \
  $WINDOWS_OPT \
   scala.tools.nsc.MainGenericRunner  "$@"

Is it correct that scala passes target/some.jar as part "$@" to java, and therefore the java command doesn't prefix target/some.jar with -jar?


Solution

  • Analysing the source code of MainGenericRunner we see scala runner tries to guess what the user intended

      def runTarget(): Option[Throwable] = howToRun match {
        case AsObject =>
          ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments)
        case AsScript if isE =>
          ScriptRunner(settings).runScriptText(combinedCode, thingToRun +: command.arguments)
        case AsScript =>
          ScriptRunner(settings).runScript(thingToRun, command.arguments)
        case AsJar    =>
          JarRunner.runJar(settings, thingToRun, command.arguments)
        case Error =>
          None
        case _  =>
          ...
      }
    

    where howToRun potentially calls guessHowToRun

      private def guessHowToRun(target: String): GenericRunnerCommand.HowToRun = {
        if (!ok) Error
        else if (io.Jar.isJarOrZip(target)) AsJar
        else if (ScalaClassLoader.classExists(settings.classpathURLs, target)) AsObject
        else {
          val f = io.File(target)
          if (!f.hasExtension("class", "jar", "zip") && f.canRead) AsScript
          else {
            Console.err.println("No such file or class on classpath: " + target)
            Error
          }
        }
      }
    

    We can be explicit by providing -howtorun flag:

    scala -howtorun:jar some.jar
    

    Scala apps can also be run via java -jar, for example, if we specify the main class in build.sbt

    Compile / mainClass := Some("example.MyMain")
    

    and execute sbt assembly, then the produced fat jar under target/ can be executed like so

    java -jar myapp-assembly.jar