31
Jan
10

the not so elegant way of creating an executable jar from scala code



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.

About these ads

8 Responses to “the not so elegant way of creating an executable jar from scala code”


  1. January 31, 2010 at 17:03

    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.

    • 2 geo
      January 31, 2010 at 17:12

      Will scala/*.class recurse into each subdirectory?

      • January 31, 2010 at 17:19

        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.

      • 4 geo
        January 31, 2010 at 17:34

        Nice! Even though I hate jar and it’s annoying quirks, this is actually useful.
        Thanks Jim!

      • January 31, 2010 at 17:44

        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.

      • 6 geo
        January 31, 2010 at 17:55

        Yeah. I already figured out how to do that using jarjar. I’m going to post the solution later today.

  2. May 1, 2011 at 10:51

    THANKS!
    You really saved me! This works wonderfully and it was just what I needed!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Blog Stats

  • 178,121 hits

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: