Building Distribution Packages

This guide explains how to build JPhotoTagger distribution packages for Linux, Windows, and macOS.

Prerequisites

The Gradle wrapper will automatically download the correct Gradle version.

Local Build Instructions

You can only build packages for your current operating system. Cross-compilation is not supported by jpackage.

Linux

./gradlew jpackage

Output: build/jpackage/JPhotoTagger/

Windows

gradlew.bat jpackage

Output: build\jpackage\JPhotoTagger\

macOS

./gradlew jpackage

Output: build/jpackage/JPhotoTagger.app/

Version Override

To build with a specific version number:

./gradlew jpackage -Pversion=2.0.0

Running the App-Image

Linux

./build/jpackage/JPhotoTagger/bin/JPhotoTagger

Windows

build\jpackage\JPhotoTagger\JPhotoTagger.exe

Or double-click JPhotoTagger.exe in File Explorer.

macOS

open build/jpackage/JPhotoTagger.app

Or double-click JPhotoTagger.app in Finder.

CI/CD Releases

Releases are built automatically using GitHub Actions.

Creating an Official Release

  1. Tag the commit with a version:

    git tag v2.0.0
    git push origin v2.0.0
    
  2. GitHub Actions will:

Creating a Test Build

  1. Go to ActionsRelease workflow
  2. Click Run workflow
  3. Check "Mark as pre-release" (default)
  4. Click Run workflow

Test builds are marked as pre-release and have dev-YYYYMMDD-HHMMSS version format.

Customization

Adding Application Icons

Create a packaging/ directory in the project root with platform-specific icons:

Platform File Format Recommended Size
Linux JPhotoTagger.png PNG 256x256 or larger
Windows JPhotoTagger.ico ICO Multi-resolution (16-256px)
macOS JPhotoTagger.icns ICNS Multi-resolution

The build will automatically detect and use these icons.

Creating icons:

Bundling Extra Files

To include additional files (manual, scripts, etc.) in the app-image:

  1. Create a resources directory for jpackage:

    mkdir -p packaging/resources
    
  2. Add files to bundle:

    cp dist_files/manual/Manual_de.pdf packaging/resources/
    cp -r dist_files/scripts packaging/resources/
    
  3. Modify build.gradle.kts jpackage task to add:

    "--resource-dir", resourceDir.resolve("resources").absolutePath
    

Adding Native Installers

By default, the build creates portable app-images. To create native installers instead:

Prerequisites by Platform

Platform Installer Type Required Tools
Linux .deb dpkg-deb (installed by default on Debian/Ubuntu)
Linux .rpm rpm-build (sudo apt install rpm or sudo dnf install rpm-build)
Windows .msi WiX Toolset 3.0+
Windows .exe WiX Toolset or Inno Setup
macOS .dmg Xcode Command Line Tools (xcode-select --install)
macOS .pkg Xcode Command Line Tools

Modifying the Build

Add an installerType property to the jpackage task in build.gradle.kts:

val installerType = project.findProperty("installerType")?.toString() ?: "app-image"

// In jpackageArgs:
"--type", installerType,

Then build with:

# Linux .deb
./gradlew jpackage -PinstallerType=deb

# Linux .rpm
./gradlew jpackage -PinstallerType=rpm

# Windows .msi
gradlew.bat jpackage -PinstallerType=msi

# macOS .dmg
./gradlew jpackage -PinstallerType=dmg

CI Considerations

To build native installers in CI, you need to install the required tools:

Linux (.deb is available by default, for .rpm):

- name: Install RPM tools
  run: sudo apt-get install -y rpm

Windows (.msi requires WiX):

- name: Install WiX Toolset
  run: choco install wixtoolset -y

macOS (.dmg/.pkg work by default with Xcode CLI tools)

Changing JVM Options

Edit the javaOptions list in build.gradle.kts:

val javaOptions = listOf(
    "-XX:+UseZGC",
    "-XX:+UseStringDeduplication",
    "-Xmx2g",        // Increase max heap
    "-Xms512m"       // Increase initial heap
)

Troubleshooting

"jpackage: command not found"

Ensure you have JDK 21+ installed (not just JRE). The jpackage tool is included in the JDK.

java -version   # Should show 21+
which jpackage  # Should show path to jpackage

Windows Defender blocks the app

Windows may block unsigned applications. Users need to click "More info" → "Run anyway" on first launch.

For production releases, consider code signing with a certificate.

macOS Gatekeeper blocks the app

Unsigned apps are blocked by default. Users can:

  1. Right-click the app → Open → Open (first launch only)
  2. Or: System Preferences → Security & Privacy → "Open Anyway"

For production releases, consider Apple Developer notarization.

Build fails with "installDist" error

Run the full build first:

./gradlew build
./gradlew jpackage