Skip to content

Commit

Permalink
Support alternative launchers for Mac apps.
Browse files Browse the repository at this point in the history
  • Loading branch information
colorizenl committed Feb 18, 2024
1 parent 9f9088c commit 3dc0786
Show file tree
Hide file tree
Showing 28 changed files with 761 additions and 337 deletions.
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ buildscript {

plugins {
id "io.freefair.lombok" version "8.4"
id "com.github.ben-manes.versions" version "0.50.0"
id "com.github.ben-manes.versions" version "0.51.0"
}

apply plugin: "java-gradle-plugin"
apply plugin: "com.gradle.plugin-publish"
apply plugin: "jacoco"

group = "nl.colorize"
version = "2024.2"
version = "2024.4"
compileJava.options.encoding = "UTF-8"

java {
Expand All @@ -39,7 +39,7 @@ dependencies {
implementation gradleApi()
implementation localGroovy()
implementation files("lib/appbundler-1.0ea.jar")
implementation "org.jsoup:jsoup:1.17.1"
implementation "org.jsoup:jsoup:1.17.2"
implementation "org.commonmark:commonmark:0.21.0"
implementation "org.nanohttpd:nanohttpd-webserver:2.3.1"
testImplementation "org.junit.jupiter:junit-jupiter:5.10.1"
Expand Down Expand Up @@ -95,7 +95,7 @@ gradlePlugin {
}
}

// Gradle has a compatibility issue with Java 17 when running tests,
// Gradle has a compatibility issue with Java 17+ when running tests,
// see https://github.com/gradle/gradle/issues/18647 for details.
tasks.withType(Test).configureEach {
jvmArgs(["--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED"])
Expand Down
20 changes: 1 addition & 19 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,14 @@ java {
sourceSets.test.java.srcDirs = ["source"]
}

repositories {
mavenCentral()
maven {
url "https://jitpack.io"
}
}

dependencies {
implementation "nl.colorize:colorize-java-commons:2024.1"
implementation "nl.colorize:multimedialib:2024.1"
}

jar {
archiveFileName = "example.jar"
archiveFileName = "example-app.jar"
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude "**/module-info.class"

manifest {
attributes "Main-Class": "com.example.ExampleApp"
}

from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}

macApplicationBundle {
Expand All @@ -61,8 +45,6 @@ macApplicationBundle {
icon = "../resources/icon.icns"
applicationCategory = "public.app-category.developer-tools"
mainClassName = "com.example.ExampleApp"
extractNatives = true
args = ["gdx"]
}

msi {
Expand Down
Binary file removed example/resources/example-gallery.png
Binary file not shown.
89 changes: 36 additions & 53 deletions example/source/ExampleApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,69 +6,52 @@

package com.example;

import nl.colorize.multimedialib.renderer.Canvas;
import nl.colorize.multimedialib.renderer.DisplayMode;
import nl.colorize.multimedialib.renderer.ErrorHandler;
import nl.colorize.multimedialib.renderer.FilePointer;
import nl.colorize.multimedialib.renderer.GraphicsMode;
import nl.colorize.multimedialib.renderer.Renderer;
import nl.colorize.multimedialib.renderer.ScaleStrategy;
import nl.colorize.multimedialib.renderer.WindowOptions;
import nl.colorize.multimedialib.renderer.java2d.Java2DRenderer;
import nl.colorize.multimedialib.renderer.libgdx.GDXRenderer;
import nl.colorize.multimedialib.stage.ColorRGB;
import nl.colorize.multimedialib.stage.Image;
import nl.colorize.multimedialib.stage.Sprite;
import nl.colorize.multimedialib.scene.Scene;
import nl.colorize.multimedialib.scene.SceneContext;
import nl.colorize.util.swing.ApplicationMenuListener;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;

/**
* Example application that displays an extremely simple MultimediaLib scene.
* This acts as a "real" application that is included in the plugin code,
* both for testing purposes and as an example on how to use the plugin.
* Example application that displays an extremely simple Swing user interface.
* This is included in the plugin code so that the plugin can be tested from
* a Gradle build.
*/
public class ExampleApp implements Scene, ApplicationMenuListener {
public class ExampleApp extends JPanel {

public static void main(String[] args) {
ExampleApp app = new ExampleApp();

Canvas canvas = new Canvas(800, 600, ScaleStrategy.flexible());
DisplayMode displayMode = new DisplayMode(canvas, 60);
private BufferedImage logo;

WindowOptions windowOptions = new WindowOptions("Example");
windowOptions.setAppMenuListener(app);

if (args.length > 0 && args[0].contains("java2d")) {
windowOptions.setTitle(windowOptions.getTitle() + " (Java2D renderer)");
Renderer renderer = new Java2DRenderer(displayMode, windowOptions);
renderer.start(app, ErrorHandler.DEFAULT);
} else {
Renderer renderer = new GDXRenderer(GraphicsMode.MODE_2D, displayMode, windowOptions);
renderer.start(app, ErrorHandler.DEFAULT);
}
}

@Override
public void start(SceneContext context) {
context.getStage().setBackgroundColor(new ColorRGB(235, 235, 235));

Image icon = context.getMediaLoader().loadImage(new FilePointer("icon.png"));
Sprite sprite = new Sprite(icon);
sprite.setPosition(context.getCanvas().getWidth() / 2f, context.getCanvas().getHeight() / 2f);
sprite.getTransform().setScale(25);
context.getStage().getRoot().addChild(sprite);
public static void main(String[] args) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(true);
window.setTitle("Example");
window.setContentPane(new ExampleApp());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
}

@Override
public void update(SceneContext context, float deltaTime) {
}
public ExampleApp() {
super();
setLayout(null);
setPreferredSize(new Dimension(800, 600));
setBackground(new Color(235, 235, 235));

@Override
public void onQuit() {
try (InputStream stream = getClass().getClassLoader().getResourceAsStream("icon.png")) {
logo = ImageIO.read(stream);
} catch (IOException e) {
throw new RuntimeException("Unable to load image", e);
}
}

@Override
public void onAbout() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(logo, getWidth() / 2 - 100, getHeight() / 2 - 100, 200, 200, null);
}
}
101 changes: 63 additions & 38 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The plugin is available from the [Gradle plugin registry](https://plugins.gradle
use the plugin in your Gradle project by adding the following to `build.gradle`:

plugins {
id "nl.colorize.gradle.application" version "2024.2"
id "nl.colorize.gradle.application" version "2024.4"
}

Building native Mac application bundles
Expand Down Expand Up @@ -81,39 +81,57 @@ The following shows an example on how to define this configuration in Gradle:

The following configuration options are available:

| Name | Required | Description |
|------------------------|----------|-----------------------------------------------------------------------|
| `name` | yes | Mac application name. |
| `displayName` | no | Optional display name, defaults to the value of `name`. |
| `identifier` | yes | Apple application identfiier, in the format "com.example.name". |
| `bundleVersion` | yes | Application bundle version number. |
| `description` | yes | Short description text. |
| `copyright` | yes | Copyright statement text. |
| `applicationCategory` | yes | Apple application category ID. |
| `minimumSystemVersion` | no | Minimum required Mac OS version number. Defaults to 10.13. |
| `architectures` | no | List of supported CPU architectures. Default is `arm64` and `x86_64`. |
| `mainClassName` | yes | Fully qualified main class name. |
| `jdkPath` | no | Location of JDK. Defaults to `JAVA_HOME`. |
| `modules` | no | List of JDK modules. An empty list will embed the entire JDK. |
| `additionalModules` | no | List of JDK modules, added without overriding the default `modules`. |
| `options` | no | List of JVM command line options. |
| `args` | no | List of command line arguments provided to the main class. |
| `startOnFirstThread` | no | When true, starts the application with `-XstartOnFirstThread`. |
| `icon` | yes | Location of the `.icns` file. |
| `extractNatives` | no | Extracts embedded native libraries from JAR files. |
| `outputDir` | no | Output directory path, defaults to `build/mac`. |

- Note that, in addition to the `bundleVersion` property, there is also the concept of build
version. This is normally the same as the bundle version, but can be manually specified for each
build by setting the `buildversion` system property.
- Signing the application bundle requires an Apple Developer account and corresponding signing
identity. The name of this identity can be set using the `MAC_SIGN_APP_IDENTITY` and
`MAC_SIGN_INSTALLER_IDENTITY` environment variables, for signing applications and installers
respectively.
- By default, the contents of the application will be based on all JAR files produces by the
project, as described by the `libsDir` property. This behavior can be replaced by setting the
`contentDir` property in the plugin's configuration. The easiest way to bundle all content,
including application binaries, resources, and libraries, is to create a single "fat JAR" file:
| Name | Required | Description |
|------------------------|----------|-----------------------------------------------------------------|
| `name` | yes | Mac application name. |
| `displayName` | no | Optional display name, defaults to the value of `name`. |
| `identifier` | yes | Apple application identfiier, in the format "com.example.name". |
| `bundleVersion` | yes | Application bundle version number. |
| `description` | yes | Short description text. |
| `copyright` | yes | Copyright statement text. |
| `applicationCategory` | yes | Apple application category ID. |
| `minimumSystemVersion` | no | Minimum required Mac OS version number. Defaults to 10.13. |
| `architectures` | no | Supported CPU architectures. Default is [`arm64`, `x86_64`]. |
| `mainJarName` | yes | File name for the JAR file containing the main class. |
| `mainClassName` | yes | Fully qualified main class name. |
| `jdkPath` | no | Location of JDK. Defaults to `JAVA_HOME`. |
| `modules` | no | Overrides list of embedded JDK modules. |
| `additionalModules` | no | Extends default list of embedded JDK modules. |
| `options` | no | List of JVM command line options. |
| `args` | no | List of command line arguments provided to the main class. |
| `startOnFirstThread` | no | When true, starts the application with `-XstartOnFirstThread`. |
| `icon` | yes | Location of the `.icns` file. |
| `launcher` | no | Generated launcher type. Either "native" (default) or "shell". |
| `signNativeLibraries` | no | Signs native libraries embedded in the application's JAR files. |
| `outputDir` | no | Output directory path, defaults to `build/mac`. |

The application bundle includes a Java runtime. This does not include the full JDK, to reduce
the bundle size. The list of JDK modules can be extended using the `additionalModules` property,
or replaced entirely using the `modules` property. By default, the following JDK modules are
included in the runtime:

- java.base
- java.desktop
- java.logging
- java.net.http
- java.sql
- jdk.crypto.ec

Mac applications use two different version numbers: The application version and the build version.
By default, both are based on the `bundleVersion` property. It is possible to specify the build
version on the command line (it's not a property since the build version is supposed to be unique
for every build). The build version can be set using the `buildversion` system property, e.g.
`gradle -Dbuildversion=1.0.1 createApplicationBundle`.

Signing the application bundle requires an Apple Developer account and corresponding signing
identity. The name of this identity can be set using the `MAC_SIGN_APP_IDENTITY` and
`MAC_SIGN_INSTALLER_IDENTITY` environment variables, for signing applications and installers
respectively.

By default, the contents of the application will be based on all JAR files produces by the
project, as described by the `libsDir` property. This behavior can be replaced by setting the
`contentDir` property in the plugin's configuration. The easiest way to bundle all content,
including application binaries, resources, and libraries, is to create a single "fat JAR" file:

```
jar {
Expand All @@ -137,8 +155,13 @@ The following configuration options are available:
The plugin adds a number of tasks to the project that use this configuration:

- **createApplicationBundle**: Creates the application bundle in the specified directory.
- **signApplicationBundle**: Signs the created application bundle and packages it into an installer
so that it can be distributed.
- **signApplicationBundle**: Signs the created application bundle and packages it into an
installer so that it can be distributed.
- **packageApplicationBundle**: An *experimental* task that creates the application bundle using
the [jpackage](https://docs.oracle.com/en/java/javase/21/docs/specs/man/jpackage.html) tool
that is included with the JDK. Creates both a DMG file and a PKG installer. This task is
experimental, it does not yet support all options from the *createApplicationBundle* and
*signApplicationBundle* tasks.

Note that the tasks are *not* added to any standard tasks such as `assemble`, as Mac application
bundles can only be created when running the build on a Mac, making the tasks incompatible with
Expand Down Expand Up @@ -167,7 +190,7 @@ are available:
| Name | Required | Description |
|-----------------|----------|-----------------------------------------------------------------|
| `inherit` | no | Inherits some configuration options from Mac app configuration. |
| `mainJarName` | no | File name of the main JAR file. Defaults to application JAR. |
| `mainJarName` | depends | File name of the main JAR file. Defaults to application JAR. |
| `mainClassName` | depends | Fully qualified main class name. |
| `options` | no | List of JVM command line options. |
| `args` | no | List of command line arguments provided to the main class. |
Expand Down Expand Up @@ -198,7 +221,7 @@ configured using the `exe` section:
| Name | Required | Description |
|---------------|----------|-----------------------------------------------------------------|
| `inherit` | no | Inherits some configuration options from Mac app configuration. |
| `mainJarName` | no | File name of the main JAR file. Defaults to application JAR. |
| `mainJarName` | depends | File name of the main JAR file. Defaults to application JAR. |
| `args` | no | List of command line arguments provided to the main class. |
| `name` | depends | Windows application name. |
| `version` | depends | Windows application version number. |
Expand Down Expand Up @@ -338,7 +361,9 @@ The plugin comes with an example application, that can be used to test the plugi
- Navigate to the `example` directory to build the example app.
- Run `gradle createApplicationBundle` to create a Mac application bundle.
- Run `gradle signApplicationBundle` to sign a Mac application bundle.
- Run `gradle packageApplicationBundle` to create a Mac application bundle using `jpackage`.
- Run `gradle packageMSI` to create a Windows MSI installer.
- Run `gradle packageEXE` to create a standalone Windows application.
- Run `gradle xcodeGen` to generate a Xcode project for a hybrid iOS app.
- Run `gradle generateStaticSite` to generate a website from Markdown templates.
- Run `gradle generatePWA` to create a PWA version of the aforementioned website.
Expand Down
42 changes: 0 additions & 42 deletions resources/config.xml

This file was deleted.

Binary file modified resources/example.jar
Binary file not shown.
15 changes: 15 additions & 0 deletions resources/launcher.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

# -----------------------------------------------------------------------------
# File generated by Colorize Gradle application plugin
# -----------------------------------------------------------------------------

LAUNCHER_DIR=$(dirname "$0")

"$LAUNCHER_DIR/../PlugIns/{{jdk}}/Contents/Home/bin/java" \
-Djava.launcher.path="$LAUNCHER_DIR" \
-Djava.library.path="$LAUNCHER_DIR" \
-Xmx2g \
-Xdock:name="{{appName}}" \
-Xdock:icon="$LAUNCHER_DIR/../Resources/icon.icns" \
-jar "$LAUNCHER_DIR/../Java/{{jarFileName}}" {{appArgs}}

0 comments on commit 3dc0786

Please sign in to comment.