For a while now I kept thinking how I would create an executable jar that would contain the Scala runtime. Tonight, I decided to give it a shot and try to make it work.
If you don’t already know, a jar is nothing but a zip file that includes a manifest ( which holds some information ).
To test this, I used the following code:
import java.io._
object Whatever
{
def main(args:Array[String]) = {
new File(args(0)).listFiles.foreach(println)
}
}
So … first we compile it to bytecode. We use scalac for that:
scalac x.scala
If you’re using the code I posted above, you should get 3 classes out of the compilation process. Next, we need to create a manifest:
Main-Class: Whatever
Please notice there is an extra line after the line with Main-Class.
Now, we need to actually create the jar. We use the jar binary that ships with your Java installation:
jar -cvfm h.jar Manifest.mf Whatever.class Whatever$$anonfun$main$1.class Whatever.class Whatever$.class
Ok, so if we now run the existing h.jar jar, you should get this exception:
Exception in thread "main" java.lang.NoClassDefFoundError: scala/ScalaObject
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:14
1)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at Whatever.main(x.scala)
Caused by: java.lang.ClassNotFoundException: scala.ScalaObject
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
... 13 more
It’s because it can’t find the Scala runtime. So … here comes the “not so elegant way” of adding the Scala runtime
. In your Scala’s installation folder, you have a folder named lib which contains a jar named scala-library.jar. I’m assuming you have some software that can extract zip archives ( I think most operating systems have this by default ). Extract that archive to some directory, and notice the scala folder. Now, open the jar we created above using your favourite compression software. You should have the following structure:

Remember the scala package I told you about a couple of lines above? Simply drag it into your archive, at the same level as the META-INF folder, like in the following picture:

After this is over, you can try to run your jar again:
java -jar h.jar .
And it should/will work. In a day or two, I’ll show you how to accomplish the same thing using jarjar.
You could also extract the contents of the Scala library jar into the current directory where you’re doing your work. Then, alter your jar command that builds your executable jar into something like:
jar -cvfm h.jar Manifest.mf *.class scala/*.class
You can add more directory paths at the end of the command-line. This should package everything together in a single jar.
Will scala/*.class recurse into each subdirectory?
It will if you leave the *.class off of the last part of the command-line I had presented:
jar -cvfm h.jar Manifest.mf *.class scala
Where “scala” is the root directory for the Scala library directory hierarchy.
Nice! Even though I hate jar and it’s annoying quirks, this is actually useful.
Thanks Jim!
You might want to look into building an Ant script that will do all of your compilation for you ( generate the Java from Scala … on success build the jar … etc. )
You can probably find a more elegant way of coupling the Scala libs into the jar with Ant as I think that it has a “zip” task that would allow you to add the Scala libs into the jar as a separate step.
The jar technique will work, but it requires you to always have a copy of the libs in your work folder for your current project. There’s probably a way to use the -C jar options to change to a common directory and include the libraries, but I don’t know how to do that offhand.
Yeah. I already figured out how to do that using jarjar. I’m going to post the solution later today.
THANKS!
You really saved me! This works wonderfully and it was just what I needed!