install4j and AppleScript: Creating a Mac OS X Application Bundle for a Java application
We have a few internal applications at Neo which can be launched using 'java -jar
My favourite installation pattern is the one where when you double click the dmg it shows you a window where you can drag the application into the 'Applications' folder, like this:
I’m not a fan of the installation wizards and the installation process here is so simple that a wizard seems overkill.
I started out learning about the structure of an application bundle which is well described in the Apple Bundle Programming guide. I then worked my way through a video which walks you through bundling a JAR file in a Mac application.
I figured that bundling a JAR was probably a solved problem and had a look at App Bundler, JAR Bundler and Iceberg before settling on Install4j which we used for Neo4j desktop.
I started out by creating an installer using Install4j and then manually copying the launcher it created into an Application bundle template but it was incredibly fiddly and I ended up with a variety of indecipherable messages in the system error log.
Eventually I realised that I didn’t need to create an installer and that what I actually wanted was a Mac OS X single bundle archive media file.
After I’d got install4j creating that for me I just needed to figure out how to create the background image telling the user to drag the application into their 'Applications' folder.
Luckily I came across this StackOverflow post which provided some AppleScript to do just that and with a bit of tweaking I ended up with the following shell script which seems to do the job: ~bash #!/bin/bash rm target/DBench_macos_1_0_0.tgz /Applications/install4j\ 5/bin/install4jc TestBench.install4j title="DemoBench" backgroundPictureName="graphs.png" applicationName="DemoBench" finalDMGName="DemoBench.dmg" rm -rf target/dmg && mkdir -p target/dmg tar -C target/dmg -xvf target/DBench_macos_1_0_0.tgz cp -r src/packaging/.background target/dmg ln -s /Applications target/dmg cd target rm "${finalDMGName}" umount -f /Volumes/"${title}" hdiutil create -volname ${title} -size 100m -srcfolder dmg/ -ov -format UDRW pack.temp.dmg device=$(hdiutil attach -readwrite -noverify -noautoopen "pack.temp.dmg" | egrep '^/dev/' | sed 1q | awk '{print $1}') sleep 5 echo ' tell application "Finder" tell disk "'${title}'" open set current view of container window to icon view set toolbar visible of container window to false set statusbar visible of container window to false set the bounds of container window to {400, 100, 885, 430} set theViewOptions to the icon view options of container window set arrangement of theViewOptions to not arranged set icon size of theViewOptions to 72 set background picture of theViewOptions to file ".background:'${backgroundPictureName}'" set position of item "'${applicationName}'" of container window to {100, 100} set position of item "Applications" of container window to {375, 100} update without registering applications delay 5 eject end tell end tell ' | osascript hdiutil detach ${device} hdiutil convert "pack.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "${finalDMGName}" rm -f pack.temp.dmg cd .. ~
To summarise, this script creates a symlink to 'Applications', puts a background image in a directory titled '.background', sets that as the background of the window and positions the symlink and application appropriately.
Et voila:
The Firefox guys wrote a couple of blog posts detailing their experiences writing an installer which were quite an interesting read as well.
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.