Mchr3k - SWTJar

Packaging SWT for OSX

To run SWT on OSX you must start the JVM with the command line argument -XstartOnFirstThread. However, there is no way to specify this argument from a JAR file. Instead, you must package your JAR as a proper OSX application. Some information about how to do this is available on other sites.

However, neither of these articles contained a complete working ant build file to automate the process and allow OSX packaging to be run on other operating systems. This article is intended to fill that gap.

Step 1 - Build your Jars

We are going to do something special for OSX so the first step is to build separate jars for Windows/Linux and OSX.

<!-- Package cross platform SWT Jar (Windows/Linux) -->
<swtjar jarfile="./build/jars/ui.jar"
		targetmainclass="org.project.gui.ProjectGUI"
		swtversion="3.6.2">
  <!-- Application Classes -->
  <fileset dir="./build/classes" includes="**/*.class" />
  
  <!-- Library Classes -->
  <zipfileset excludes="META-INF/*.MF" src="lib/miglayout-3.7.3.1-swt.jar"/>
  
  <!-- SWT Jars (Windows/Linux) -->
  <fileset dir="./lib" includes="swt-lin*-3.6.2.jar" />
  <fileset dir="./lib" includes="swt-win*-3.6.2.jar" />
</swtjar>

<!-- Package OSX SWT Jar -->
<swtjar jarfile="./build/jars/ui-osx.jar"
		targetmainclass="org.project.gui.ProjectGUI"
		swtversion="3.6.2">
  <!-- Application Classes -->
  <fileset dir="./build/classes" includes="**/*.class" />
  
  <!-- Library Classes -->
  <zipfileset excludes="META-INF/*.MF" src="lib/miglayout-3.7.3.1-swt.jar"/>
  
  <!-- SWT Jars (OSX) -->
  <fileset dir="./lib" includes="swt-osx*-3.6.2.jar" />
</swtjar>

Step 2 - Build .app Folder

OSX applications are packaged into folders with names ending in ".app". The contents of the folder have a very particular structure and include an Info.plist file which is where the -XstartOnFirstThread argument can be specified.

Thankfully, there is a free ant task which automated the process of building the .app folder: JarBundler

To use this task, you need to add the following at the top of your build.xml file.

<taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler" 
         classpath="./lib/jarbundler-2.2.0.jar"/>

The task can then be invoked as follows.

<mkdir dir="./build/jar/UIApp/" />
<jarbundler dir="./build/jar/UIApp/"
            name="UI"
            mainclass="org.swtjar.SWTLoader" 
            jar="./build/jars/ui-osx.jar" 
            startOnMainThread="true" 
            icon="./icons/ui128.icns" 
            stubfile="./lib/JavaApplicationStub" />

There are several things to note about these parameters.

  • dir - This is the directory under which the .app folder will be constructed. In this case you will end up with a folder /build/jar/UIApp/UI.app/.
  • name - This is the name of the .app folder which will be created.
  • mainclass - This is the main entry point for the cross platform SWT Loader. This is still needed to select the correct 32/64 bit OSX SWT Jar.
  • startOnMainThread - This ensured the extra JVM argument is set to allow SWT to be loaded correctly.
  • icon - This is an optional param. The icon must be in the icns format but there are multiple sites which will convert other formats such as PNG to an icns file. I have used this site: http://iconverticons.com/
  • stubfile - This is the wrapper executable which launches your application. The JarBundler site has the following to say:
    "When the JarBundler ANT task is used under Windows or Linux, a copy of the JavaApplicationStub must be supplied by the developer. This cannot be built from source but must be obtained from a Mac OS X system."
    If you don't have access to an OSX system, I have a copy checked in from an OSX 10.6.6 system here.

You now have a .app folder which could be used to correctly launch your SWT UI on OSX. However, it isn't very practical to distribute a folder so there is one more step to package this up.

Step 3 - Package .app Folder

The correct way to do this would be to package the .app folder as an OSX "Disk Image". However, I could not find a way of doing this packaging on any platform apart from OSX. Therefore, I chose to simply package the folder in a .tar file. Using tar instead of zip is a deliberate choice as tar files allow permissions to be stored to ensure the .app folder is executable after being untarred.

Here is the ant build.xml code.

<delete file="./build/jar/UIApp/UI.app/Contents/MacOS/JavaApplicationStub" />
<mkdir dir="./build/jar/UILauncher/UI.app/Contents/MacOS/" />
<copy file="./lib/JavaApplicationStub" 
      todir="./build/jar/UILauncher/UI.app/Contents/MacOS/" />
<tar destfile="./build/jars/UI.app.tar">
  <tarfileset dir="./build/jars/UIAppLauncher/" filemode="777" />
  <tarfileset dir="./build/jars/UIApp/" />
</tar>

This is doing the following.

  • Delete the JavaApplicationStub from the .app folder build by JarBundler.
  • Create a new directory with the same structure as the .app folder.
  • Copy the JavaApplicationStub into this new directory.
  • Build a jar file and mark the JavaApplicationStub with permissions 777 to ensure it is executable when untarred.

And thats it - you now have a single file UI.app.tar which can be distributed to OSX users.