How to build a Mac Java app (OS X 10.9+ and Java 7+)

UPDATE: The solution below worked for older versions of Java, and here’s the new solution for bundling Java 14 and newer applications on macOS systems.

Although I originally wrote this article for earlier versions of macOS and Java, I just built a similar Mac/Java application in June, 2017, using Java 1.8.x and macOS Sierra 10.12.5.

As a quick note to self, I used the following Ant build.xml file in 2014 to build my Wikipedia Reader client application on Mac OS X 10.9 with Java 7.x:

<project name="WikiReader" default="create-bundle" basedir=".">

  <taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask"/>
  <property environment="env"/>

  <!-- input stuff -->
  <property name="current.dir" value="." />
  <property name="scala-lib.dir" value="scala-libs" />

  <!-- output stuff -->
  <property name="release.dir" value="release" />

  <!-- clean -->
  <target name="clean">
    <echo message="clean task ..." />
    <!-- just needed for the first-time run -->
    <mkdir dir="${release.dir}"/>
    <!-- remove the old version of the app -->
    <delete dir="${release.dir}/WikiReader.app" />
  </target>

  <!-- create mac osx 'bundle' -->
  <target name="create-bundle" depends="clean">
  <bundleapp outputdirectory="${release.dir}"
        name="WikiReader"
        displayname="WikiReader"
        identifier="com.alvinalexander.wikireader.client.Client"
        shortversion="0.1"
        icon="WikiReader.icns"
        copyright="Alvin J. Alexander"
        applicationCategory="public.app-category.utilities"
        mainclassname="com/alvinalexander/wikireader/client/Client">

        <runtime dir="${env.JAVA_HOME}" />

        <!-- the only jar file needed when using sbt-assembly -->
        <classpath file="../target/scala-2.10/WikiReaderClient-assembly-0.1.jar" />

        <option value="-Xdock:icon=Contents/Resources/WikiReader.icns"/>
        <option value="-Dapple.laf.useScreenMenuBar=true"/>
        <option value="-Dcom.apple.macos.use-file-dialog-packages=true"/>
        <option value="-Dcom.apple.macos.useScreenMenuBar=true"/>
        <option value="-Dapple.awt.application.name=WikiReader"/>
        <option value="-Dcom.apple.smallTabs=true"/>

    </bundleapp>
  </target>

</project>

Once my Scala/Java application is built with this build script, I can run it just like any other Mac OS X application.

Note that I use sbt-assembly to compile my Scala application and convert it into one jar file before running this Ant build script. You can see it referenced as the WikiReaderClient-assembly-0.1.jar file.

(In the real world I have a shell script that (a) builds the application with SBT and sbt-assembly, and (b) runs Ant with this build script to (c) bundle my Mac application. You might be able to do all of that with SBT alone, but I haven’t looked into that.)

This script uses the Oracle “appbundler” technology that’s built into their OS X JDK. You can read more about how this works at this Oracle/Java URL.

Update: In June, 2017, I learned that JAVA_HOME must be set for Ant and AppBundler to work. I show a good way to set it in this How to set the Java version on macOS systems tutorial.

Note: Should not need rt.jar

I have seen some people write that they have to manually copy the rt.jar file that comes with JDK 1.8 into their project, such as copying that file into their project’s lib folder. That shouldn’t be necessary when using AppBundler. If you use the find command to search for jar files under your project’s build directory, you should see it included, like this:

$ find release/WikiStar.app -name "*jar"

release/WikiStar.app/Contents/Java/WikiStar-assembly-1.0.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/charsets.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/cldrdata.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/dnsns.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/jaccess.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/jfxrt.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/localedata.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/nashorn.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/sunec.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext/zipfs.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jce.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jfr.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jfxswt.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jsse.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/management-agent.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/resources.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/security/local_policy.jar
release/WikiStar.app/Contents/PlugIns/jdk1.8.0_131.jdk/Contents/Home/jre/lib/security/US_export_policy.jar

Again, this is still true with Java 1.8 on macOS 10.12.5.