Virts

Virts

私は可愛いです 🥰 お金をください ❤️
telegram
github
email
steam
douban

How to elegantly package a Jar into a Mac App

Requirements#

Recently, I've started playing with penetration testing again, and many of the tools I use come as a .jar file package. Although some can be opened directly by double-clicking, the icons in the Dock cannot be changed. Some can only run with Java 8, and I have to open the command line, switch JAVA_HOME, and then run the Java command to start it, which doesn't feel very elegant.

My previous approach was based on the automation tool Automator provided by MacOS, which wraps a Bash script into an App. The script needs to have the JAVA_HOME path written correctly, and then run the .jar file. However, this method is also not very elegant; it cannot customize the Dock icon, and there will be a spinning circle in the top menu bar. If several are opened, there will be multiple spinning circles, which is also not very elegant.

I Googled for solutions, but most tools are outdated versions, and some do not support the ARM version. However, I found a GitHub project: universalJavaApplicationStub, which uses Bash as a launcher to start Jar files, but it can use the Bash script as a kernel to directly wrap the Jar file into a native MacOS App. Manual testing of the wrapped App program shows that it can run.

The general steps refer to this blog post, and the manual operation steps are as follows:

  1. Create folders like /Applications/MyApp.app/Contents/{MacOS,Resources}.
  2. Write the Info.plist file, which defines the app name, version number, icon, and other meta information.
  3. Place the universalJavaApplicationStub file, icon file, and Jar file into the corresponding folders.

In this way, an App is packaged, although the above steps are very simplified. In reality, the process is quite cumbersome, so it is better to wrap these steps into a command-line program that specifies configuration information through parameters, converting a Jar file into an App application with a single command.

Thus, I implemented jar2app in Golang. The basic principle is actually the above steps, but it is automated through code, requiring only a few parameters to be specified.

How it works#

jar2app has the following features:

  1. Convenience: Easy to configure, requiring only a few parameters to wrap a Jar file into a MacOS App application.
  2. Efficiency: Runs quickly, with packaging time usually not exceeding 1 second.
  3. Simplicity: Compact size, the packaged App size is roughly the same as the original Jar file, unlike Electron, which makes applications bulky.
  4. Usability: Simple installation, standalone binary file, can be installed and upgraded with a single command via brew.

Here are some notes

Because the structure of EXE files on Windows is completely different from that on MacOS, even though Golang can cross-compile across platforms, jar2app can only generate MacOS .app format applications. Of course, you can also generate a MacOS App on Windows.

Considering the size of the packaged file, jar2app does not package the entire Java Runtime into the application, so before using jar2app, please install the corresponding Java version first. It is recommended to install multiple Java versions simultaneously to test whether the Jar file can run smoothly.

When packaging, if the $JAVA_HOME is not set in the current environment variables, jar2app will automatically recognize the current environment's Java program's $JAVA_HOME as the Java version for the App. If it cannot be recognized, the packaging will fail. If you need to specify a specific Java path, please refer to Usage.

Installation#

You can install it directly via brew.

brew install PriateXYF/tap/jar2app

jar2app is implemented in Golang. If you have a Go development environment, you can install it via go install.

go install -v github.com/PriateXYF/jar2app@latest

Usage#

Parameter description:

Usage of jar2app:
  -jar string
    	.jar file path [required]
  -name string
    	app name [defaults to the same as the jar file name]
  -icon string
    	.icns icon file path [defaults to empty icon]
  -copyright string
    	app copyright information [defaults to Copyright 2025 virts]
  -id string
    	app identifier information [defaults to app.virts]
  -info string
    	app hint information [defaults to Made by virts.]
  -v string
    	app version number [defaults to 1.0.0]
  • Quick Start simplest usage
jar2app --jar MyApp.jar --icon MyApp.icns --name MyApp
  • If you need to specify a specific JAVA_HOME
JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home/" jar2app --jar MyApp.jar --icon MyApp.icns --name MyApp
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home/
jar2app --jar MyApp.jar --icon MyApp.icns --name MyApp
  • Full parameter example
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home/
jar2app --jar MyApp.jar --icon MyApp.icns --name MyApp --info "My App" --v "1.0.0" --id "app.virts" --copyright "Copyright 2025 virts"

If executed successfully, a corresponding .app application will be generated in the current path, which can be opened directly by double-clicking. Enjoy it ~~~

Example#

Taking the ice shell Behinder as an example, I will explain how to use jar2app and how to modify the configuration when additional parameters need to be added.

First, you can download the latest Jar package from the ice shell's Github Release page.

After downloading and extracting, you will find that it is not a single Jar file, but also contains other folders and files. These files can be processed later; for now, we only need to focus on the Behinder.jar file.

image

Navigate to that folder in the command line, and I tried to run Behinder.jar with the default Java version 23.0.1, but encountered an error. Switching to Java 8 allowed it to run normally.

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home/
java -jar Behinder.jar

Since it can run normally and the JAVA_HOME for executing the jar is configured correctly in this shell, I can directly use jar2app to package it. First, I randomly generated an icon using AI.

-w200

Then convert this icon to icns format, recommended to use makeicns for conversion.

brew install makeicns
makeicns -in behinder.png -out behinder.icns

Next, I can use jar2app to package the program.

jar2app --jar Behinder.jar --icon behinder.icns

At this point, if the configuration is correct, a Behinder.app application will be generated in the current path, but it still cannot be opened by double-clicking.

Since jar2app only places the individual Jar file in the corresponding application's Content/Java path, it may not start due to missing some files or folders.

Move all contents from the extracted Behinder_v4.1.t00ls into Behinder.app/Content/Java, and try double-clicking the application again. It should start normally.

Drag Behinder.app to the /Applications folder, and the installation is successful. Enjoy it ~~~


Additionally, jar2app does not currently support adding extra parameters. If you need to specify additional parameters to run, please manually modify the universalJavaApplicationStub script inside Behinder.app/Content/MacOS, adding the required parameters at the end of the file.

Reference#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.