Example: How to use javapackager to build a MacOS application bundle

I recently learned how to use the Java javapackager command to build a macOS application bundle — i.e., a regular macOS application — from a Java application. In this tutorial I’ll show how to create a Mac application bundle from a simple Java class, in this case a Java Swing class.

Back to top

Building a MacOS application bundle with javapackager

The short story of how to build a macOS application bundle using javapackager in this project is to use these steps.

First, clone my Github project onto your macOS system:

Next, move into that directory. To compile and run the project, run these commands:

  • Compile the Java class with ./1compile.sh
  • Create a JAR file from the *.class files and manifest with./2makeJar
  • Build the Mac application bundle with ./3build

Assuming that everything works as it should, you can then run the newly-built application using this command:

  • open release/bundles/MacJavaProperties.app/

When you run the application like this, you should see the following Mac/Java application open up:

A macOS application bundle built with javapackager

Back to top

The longer story

For the longer version of the story, read on ...

Back to top

The Mac/Java class

I named the Java class file MacJavaPropertiesApp.java, and its source code looks like this:

import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import java.util.Map;
import java.util.Properties;
import java.util.Enumeration;

public class MacJavaPropertiesApp {

    public static void main(String[] args) {
        new MacJavaPropertiesApp();
    }

    public MacJavaPropertiesApp() {
        SwingUtilities.invokeLater(() -> {

            // system properties
            String s = "\nSYSTEM PROPERTIES\n";
            Properties p = System.getProperties();
            Enumeration keys = p.keys();
            while (keys.hasMoreElements()) {
                String key = (String)keys.nextElement();
                String value = (String)p.get(key);
                System.out.println(key + ": " + value);
                s = s + key + ": " + value + "\n";
            }

            // environment variables
            s = s + "\n\nENVIRONMENT VARIABLES\n";
            Map<String, String> env = System.getenv();
            for (String envName : env.keySet()) {
                s = s + "\n";
                s = s + envName + ": " + env.get(envName);
            }
    
            JTextArea textArea = new JTextArea();
            textArea.setText(s);
            JScrollPane scrollPane = new JScrollPane(textArea);

            JFrame frame = new JFrame("Mac/Java Properties");
            frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(new Dimension(600, 400));
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

}
Back to top

The three scripts

The first script is named 1compile.sh, and here’s its source code:

javac MacJavaPropertiesApp.java
echo "compiled MacJavaPropertiesApp.java"

The second script is named 2makeJar, and here’s its source code:

jar cmf manifest MacJavaPropertiesApp.jar *.class
echo "created MacJavaPropertiesApp.jar from MacJavaPropertiesApp.java and manifest"

The third script is named 3build. It contains the javapackager command, and here’s its source code:

# SEE https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html

JAVA_HOME=`/usr/libexec/java_home -v 1.8`
APP_DIR_NAME=MacJavaProperties.app

#-deploy -Bruntime=/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home \
javapackager \
  -deploy -Bruntime=${JAVA_HOME} \
  -native image \
  -srcdir . \
  -srcfiles MacJavaPropertiesApp.jar \
  -outdir release \
  -outfile ${APP_DIR_NAME} \
  -appclass MacJavaPropertiesApp \
  -name "MacJavaProperties" \
  -title "MacJavaProperties" \
  -nosign \
  -v

echo ""
echo "If that succeeded, it created \"release/bundles/${APP_DIR_NAME}\""
Back to top

javapackager notes

There are about 100 things I could say about the javapackager command, but here are a few notes:

  • I don’t specify an application, but you declare one with icon=path
  • If you want to sign your application, that takes much more work, including needing an Apple developer account, and generating certificates
  • You can also specify JVM options and Mac-specific options

Those are the first things that come to mind. I’ll add more notes as I think about them.

One final note: This is intended to be a relatively simple javapackager example. As I mentioned, there are many, many more things to say about it, but this example shows how to create a macOS application bundle from Java source code, which is what I wanted to show today. (More complicated examples will have multiple JAR files, application resource files, etc.)

Back to top

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.