"scala.runtime in compiler mirror not found" but working when started with -Xbootclasspath/p:scala-library.jar
Asked Answered
S

1

13

I'm trying to run a Scala application packed as JAR (including dependencies) but this fails until the Scala library is added by using the -Xbootclasspath/p option.

Failing invocation:

java -jar /path/to/target/scala-2.10/application-assembly-1.0.jar

After the application did some of its intended output, the console shows:

Exception in thread "main" scala.reflect.internal.MissingRequirementError: object scala.runtime in compiler mirror not found. at scala.reflect.internal.MissingRequirementError$.signal(MissingRequirementError.scala:16) at scala.reflect.internal.MissingRequirementError$.notFound(MissingRequirementError.scala:17) at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:48) at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:40) at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:61) at scala.reflect.internal.Mirrors$RootsBase.getPackage(Mirrors.scala:172) at scala.reflect.internal.Mirrors$RootsBase.getRequiredPackage(Mirrors.scala:175) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackage$lzycompute(Definitions.scala:181) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackage(Definitions.scala:181) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackageClass$lzycompute(Definitions.scala:182) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackageClass(Definitions.scala:182) at scala.reflect.internal.Definitions$DefinitionsClass.AnnotationDefaultAttr$lzycompute(Definitions.scala:1015) at scala.reflect.internal.Definitions$DefinitionsClass.AnnotationDefaultAttr(Definitions.scala:1014) at scala.reflect.internal.Definitions$DefinitionsClass.syntheticCoreClasses$lzycompute(Definitions.scala:1144) at scala.reflect.internal.Definitions$DefinitionsClass.syntheticCoreClasses(Definitions.scala:1143) at scala.reflect.internal.Definitions$DefinitionsClass.symbolsNotPresentInBytecode$lzycompute(Definitions.scala:1187) at scala.reflect.internal.Definitions$DefinitionsClass.symbolsNotPresentInBytecode(Definitions.scala:1187) at scala.reflect.internal.Definitions$DefinitionsClass.init(Definitions.scala:1252) at scala.tools.nsc.Global$Run.(Global.scala:1290) at extract.ScalaExtractor$Compiler$2$.(ScalaExtractor.scala:24)

Working invocation:

java -Xbootclasspath/p:/path/to/home/.sbt/boot/scala-2.10.2/lib/scala-library.jar -jar /path/to/target/scala-2.10/application-assembly-1.0.jar

The strange thing about it is that the application-assembly-1.0.jar was built so that it includes all dependencies including the Scala library. When one extracts the JAR file it can be verified that the class files in the scala.runtime package have been included.

Creation of the JAR file

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.1") was added to project/plugins.sbt and the assembly target was invoked. A JAR file of about 25MB results.

Building the JAR with proguard shows the same runtime behavior as seen with assembly's JAR file.

Application code that triggers the MissingRequirementError

Some application code works fine and the previously described exception is triggered as soon as the new Run from the following fragment executes.

import scala.reflect.internal.util.BatchSourceFile
import scala.reflect.io.AbstractFile
import scala.reflect.io.Path.jfile2path
import scala.tools.nsc.Global
import scala.tools.nsc.Settings
…
import scala.tools.nsc._
object Compiler extends Global(new Settings()) {
  new Run // This is line 24 from the stack trace!

  def parse(path: File) = {
    val code = AbstractFile.getFile(path)
    val bfs = new BatchSourceFile(code, code.toCharArray)
    val parser = new syntaxAnalyzer.UnitParser(new CompilationUnit(bfs))
    parser.smartParse()
  }
}
val ast = Compiler.parse(file)

Among others, scala-library, scala-compiler and scala-reflect are defined as dependencies in build.sbt.

For the curios / background information

The aim of the application is to aid in localization of Java and Scala programs. The task of the code fragment above is to get an AST from a Scala file in order to find method calls in there.

The questions

  • Given the Scala library is included in the JAR file, why is necessary to call the JAR using -Xbootclasspath/p:scala-library.jar?
  • Why do other parts of the application run just fine even though scala.runtime is reported as missing later?
Suppositive answered 9/8, 2013 at 16:0 Comment(3)
this might be because you have not given any classpath to that compiler run to use. Instead of Global(new Settings()), try giving it a settings that has the same classpath as your program: settings.classpath.value = System.getProperty("java.class.path").Monopolize
or maybe settings.usejavacp.value = true, or something similar...Monopolize
Thanks a lot for your comments. It seems I've wrongly assumed the compiler settings would default to those of the invoking program. Adding either settings.usejavacp.value = true or settings processArgumentString "-usejavacp" will result in a correctly configured compiler and thus a working program.Suppositive
W
10

The easy way to configure the settings with familiar keystrokes:

  import scala.tools.nsc.Global
  import scala.tools.nsc.Settings
  def main(args: Array[String]) {
    val s = new Settings
    s processArgumentString "-usejavacp"
    val g = new Global(s)
    val r = new g.Run
  }

That works for your scenario.

Even easier:

java -Dscala.usejavacp=true -jar ./scall.jar

Bonus info, I happened to come across the enabling commit message:

Went ahead and implemented classpaths as described in email to scala-internals on the theory that at this point I must know what I'm doing.

** PUBLIC SERVICE ANNOUNCEMENT **

If your code of whatever kind stopped working with this commit (most likely the error is something like "object scala not found") you can get it working again with either of:

passing -usejavacp on the command line

set system property "scala.usejavacp" to "true"

Either of these will alert scala that you want the java application classpath to be utilized by scala as well.

Wheen answered 9/8, 2013 at 17:21 Comment(3)
To anyone who gets a similar error when configuring Scala in Intellij IDEA, add this to "Additional compiler options" in Project Structure -> Facets -> Scala -> Compiler Options: "-usejavacp" I successuflly used this to solve fatal error: object scala.beans.BeanInfo in compiler mirror not found.Hypoglossal
@Malcolm, I get to facets but then I cannot find Scala. I see no facets are configured. Press "+" to add a new facet. When I click +, Scala is not one of the optionsOpisthognathous
Hmmm... do you have the Intellij IDEA Scala plugin? plugins.jetbrains.com/plugin/1347-scalaHypoglossal

© 2022 - 2024 — McMap. All rights reserved.