Building a Single File Scala Executable

I'm from a C background with a hefty dose of Python, mostly on linux systems.

My preferred mode of delivering software is as a single executable file. I hate having to explain to users that they have to install a half-dozen support libraries. (This is less painful on modern linux systems where I can tell them to do the equivalent of apt-get install library1 library2 ... or just ship a deb/rpm/etc that explicitly contains the requirements.)

This doesn't seem to be the normal way of doing things in the Java world. (It's often not that simple in Python either, so I accept that I will have to make adjustments.)

The first Scala project I'm working on is a simple static site generator -- essentially to replace my use of Pelican with my own program in Scala. It turns out this is very easy to do with Laika, which looks like it will do most of the work for me.

So my first challenge was producing a file that I could actually run like a normal system command, without messing around with a long java command line, classpaths, etc. And without having to tell users that they have to install Laika or whatever other dependencies I end up with. This is fairly easy with sbt-assembly; the following instructions are largely copied from the sbt-assembly README.

  1. Create $root/project/assembly.sbt with the following content:

    addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.10.1")
    
  2. Add import AssemblyKeys._ at the top of $root/build.sbt.

  3. Add mainClass in assembly := Some("my.main.Class") at the bottom of $root/build.sbt.

  4. Create $root/assembly.sbt with the following content:

    import AssemblyKeys._
    
    assemblySettings
    
  5. Run sbt assembly and get a jar file in target/scala-2.10/my-assembly-1.0.jar

  6. Run the file with java -jar target/scala-2.10/my-assembly-1.0.jar.

I tried to take it another step further and make the jar file self-executable by prepending a shell script. This is possible with zip files (jars are just zip files) because the important info is at the end of the archive and anything can be prepended. But apparently java is stricter about the format because this doesn't work -- java complains "Invalid or corrupt jarfile".

I'll have to settle for a (two-line) wrapper script. I consider it a success that I was able to take the single jar from above, copy it to a machine that didn't have Scala or Laika installed, and it ran just fine.

Posted on 2013-12-07 by brian in scala .
Comments on this post are closed. If you have something to share, please send me email.