Gradle plugin that builds native or hybrid applications for different platforms. It supports multiple application types for different source technologies and target platforms:
Application type | Source technology | Target platforms |
---|---|---|
Native Mac application bundle | Java | Mac |
Native Windows MSI | Java | Windows |
Native Windows EXE | Java | Windows |
Hybrid iOS app Xcode project | Swift, web app | iOS |
PWA | Web app | iOS, Android, Windows |
Static site | Web app, Markdown | Web |
This allows you to distribute the same application to multiple platforms, without having to maintain platform-specific code.
This plugin is basically a Gradle plugin interface on top of the native toolchain for these platforms. That unfortunately means the plugin requires the native toolchain for each platform to be available in the build environment:
Target platform | Build platform(s) | Requires |
---|---|---|
Windows MSI | Windows | WIX Toolset |
Windows EXE | Windows, Mac, Linux | Launch4j |
Mac | Mac | Xcode |
iOS | Mac | Xcode, XcodeGen |
Android | Windows, Mac, Linux | Android SDK |
The plugin is available from the Gradle plugin registry. You can
use the plugin in your Gradle project by adding the following to build.gradle
:
plugins {
id "nl.colorize.gradle.application" version "2024.7"
}
The plugin can create Mac OS application bundles. This uses the Ant plugin by TheInfiniteKind, but makes it available via Gradle. It also adds the ability to sign the application bundle and package it within an installer.
The Java runtime will be packaged with the application. By default, this will use the system JDK
as indicated by the JAVA_HOME
environment variable. This can be changed to a list of custom
JDK paths by setting the jdkPaths
configuration property. If an x86 JDK is used, generated
application bundles will support both Intel and Apple Silicon Macs. If an ARM JDK is used, only
Apple Silicon Macs will be supported.
The application bundle can be configured using the macApplicationBundle
block. The configuration
options correspong to the ones from the Ant plugin.
The following shows an example on how to define this configuration in Gradle:
macApplicationBundle {
name = "Example"
identifier = "com.example"
description = "A description for your application"
copyright = "Copyright 2024"
bundleVersion = "1.0"
icon = "resources/icon.icns"
applicationCategory = "public.app-category.developer-tools"
mainClassName = "com.example.app.Main"
outputDir = "${buildDir}"
options = ["-Xmx2g"]
}
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 | 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. |
icon |
yes | Location of the .icns file. |
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.
You will need to provide a "fat JAR" that defines a main class, and contains both your application and all of its dependencies. The following example shows how to turn your project's default JAR file into a fat JAR:
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude "**/module-info.class"
exclude "**/META-INF/INDEX.LIST"
exclude "**/META-INF/*.SF"
exclude "**/META-INF/*.DSA"
exclude "**/META-INF/*.RSA"
manifest {
attributes "Main-Class": "com.example.ExampleApp"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
There are alternative ways to create a fat JAR, if you need to retain the project's "normal" JAR file: You can create a Gradle task that will create the fat JAR in in addition to the normal JAR. You can also use the Gradle shadow JAR plugin to achieve the same effect.
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.
- packageApplicationBundle: An experimental task that creates the application bundle using the jpackage 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
some workflows (i.e. continuous integration pipelines that tend to use Linux servers).
Signing the application bundle is mandatory for distributing the application. This in turn needs
a valid Apple developer account, and corresponding certificates. You will need two certificates:
one for the application, and one for the installer. Please do not hard-code the signing identity
into the build file. It is better to define them in the Gradle properties file at
~/.gradle/gradle.properties
and then access them from there.
Uses jpackage
to generate MSI installers, so that applications can be distributed via the
Microsoft Store. The downside is that using jpackage
requires the
WIX Toolset, which only supports Windows.
First, make sure the build produces a "fat JAR", using the same instructions as described for the native Mac app. The packageMSI task can then be used to create a MSI installer that includes both the fat JAR and the Java runtime.
The plugin can be configured using the msi
section. The following configuration options
are available:
Name | Required | Description |
---|---|---|
inherit |
no | Inherits some configuration options from Mac app configuration. |
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. |
name |
depends | Windows application name. |
version |
depends | Windows application version number. |
vendor |
yes | Vendor display name. |
description |
depends | Short description text. |
copyright |
depends | Copyright statement text. |
icon |
yes | Location of .ico file. |
uuid |
yes | Windows update UUID, must remain the same across versions. |
outputDir |
no | Output directory path, defaults to build/windows-msi . |
The inherit
option can help to avoid duplicated configuration. When enabled, the msi
configuration will use matching configuration options defined in the macApplicationBundle
configuration.
Creates a native Windows .exe
file, then packages that file with both the application and an
embedded Java runtime to create a Windows application. This is an alternative for creating Windows
MSI installers, which is less portable but has the benefit that it supports non-Windows build
platforms. The .exe
file is created using Launch4j.
The packageEXE task will create both the .exe
file and the Windows application. It can be
configured using the exe
section:
Name | Required | Description |
---|---|---|
inherit |
no | Inherits some configuration options from Mac app configuration. |
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. |
icon |
yes | Location of .ico file. |
supportURL |
yes | Shown in case of application launch errors. |
memory |
no | Maximum application memory in megabytes. Defaults to 2048 MB. |
exeFileName |
no | File name for .exe file. Based on JAR file name if omitted. |
The inherit
option can help to avoid duplicated configuration. When enabled, the exe
configuration will use matching configuration options defined in the macApplicationBundle
configuration.
The Windows application contain an embedded Java runtime, which is located using the
EMBEDDED_WINDOWS_JAVA
environment variable. This directory needs to contain a subdirectory
named java
, which will be embedded in the Windows application. The configuration intentionally
does not use JAVA_HOME
, so that the Windows application can also be created on non-Windows
build platforms.
The location of Launch4j can be specified using the LAUNCH4J_HOME
environment variable.
The plugin can generate Xcode projects for hybrid iOS apps, that consist of a Swift app with a web view, that in turn displays the original web application. The web application files are packaged within the app, they are not loaded from an external server. This approach is similar to Cordova, but is more lightweight and assumes all required native functionality is implemented in Swift.
Generating the Xcode project can be done using the xcodeGen
task. This will start
XcodeGen, which means that XcodeGen needs to be available
when using this task.
The following configuration options are available via the xcode
section:
Name | Required | Description |
---|---|---|
appId |
yes | App ID in the form MyApp. |
bundleId |
yes | Apple bundleID in the form com.example. |
appName |
yes | App display name in the form My App. |
bundleVersion |
yes | Version number in the form 1.2.3. |
icon |
yes | PNG file that will be used to generate the app icons. |
iconBackgroundColor |
no | Color used to replace the icon alpha channel, e.g. #000000. |
resourcesDir |
yes | Directory to copy into the app's resources. |
launchScreenColor |
no | Background color for the app's launch screen. |
outputDir |
no | Directory for the Xcode project, defaults to build/xcode . |
xcodeGenPath |
no | XcodeGen install location, defaults to /usr/local/bin/xcodegen . |
Like the Mac application bundle, the buildversion
system property can be used to set the build
version during the build. If this system property is not present, the build version is the same
as the app version.
It is common for hybrid mobile apps to feature some kind of interaction between the native code and the web application's JavaScript. The generated Xcode project therefore includes some bindings for common tasks, which can be called from JavaScript:
clrz.openNativeBrowser(url)
opens a link in the native browser (i.e. Mobile Safari), rather than in the web view. Note that external links will be opened in the native browser by default.clrz.loadPreferences()
will load the native app store into the web application's local storage. This can be used for persistent data, since iOS aggressively cleans up local storage for infrequently used apps.clrz.savePreferences(name, value)
saves a name/value pairs to the native app storage.clrz.requestNotifications()
requests permission to schedule notifications. If the user has already previously approved or rejected permission, calling this function does nothing.clrz.scheduleNotification(id, title, preview, schedule)
schedules a native notification for the specified date and time. Theschedule
argument should be a date/time in ISO 8601 format, for example "2024-10-07 10:37:00". The date is interpreted against the user's current time zone.clrz.cancelNotification(id)
cancels a previously scheduled notification.
The plugin can also repackage web applications into Progressive Web Applications. The PWA can then in turn be repackaged into native applications using services like PWA Builder.
The generatePWA task creates a PWA from an existing web application. It will insert the web app manifest into the HTML, and also insert HTML that registers a service worker.
These tasks share the same configuration via the pwa
configuration section. The following
configuration options are available:
Name | Required | Description |
---|---|---|
webAppDir |
yes | Input directory where the original web app is located. |
outputDir |
no | Output directory for the generated PWA, defaults to build/pwa . |
manifest |
yes | Location of the web app manifest JSON file. |
serviceWorker |
no | Location of service worker file, will use default if omitted. |
cacheName |
yes | Name of the cache the service worker will use. |
The plugin can take content written in Markdown or plain HTML, and then render this content using templates to create a static site. This is done using the generateStaticSite task, which uses the following logic:
- The output directory structure will match the content directory structure.
- Each directory can include a file called
template.html
. - The template contains a tag
<clrz-content>
, the contents of this tag will be replaced with the article content. - For HTML files, the file content is used as the article.
- For Markdown files, the file is first rendered to HTML, which is then used as the article.
- Subdirectories can contain their own
template.html
file. If so, the process described above is repeated recursively. - All files except HTML and Markdown are retained in the static site.
The static site is configured using the staticSite
configuration section. The following options
are available:
Name | Required | Default | Description |
---|---|---|---|
contentDir |
no | content | Content directory, relative to project directory. |
outputDir |
no | staticsite | Output directory, relative to build directory. |
templateFileName |
no | template.html | File name used as template instead of content. |
The serveStaticSite task can be used to test the generated static site using a local HTTP server, at http://localhost:7777.
Building the plugin itself can only be done on Mac OS. It also requires the following:
The following Gradle build tasks are available:
gradle clean
cleans the build directorygradle assemble
creates the JAR file for distributiongradle test
runs all unit testsgradle coverage
runs all unit tests and reports on test coveragegradle publishPlugins
publishes the plugin to the Gradle plugin portal (requires account)
Some tests can only run on Windows or Mac. Add -Pheadless=true
when running in a headless
environment to exclude those tests.
Note: Running the tests currently requires some additional JVM properties on Java 17+, due to a Gradle issue. These properties are automatically used when running from Gradle, but need to be added manually when running tests from an IDE.
The plugin comes with an example application, that can be used to test the plugin on itself:
- First run
gradle assemble
to build the plugin itself. - 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 usingjpackage
. - 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.
- Run
Building the example application uses the same system requirements and environment variables as the plugin itself. Refer to the documentation for each application type for details.
Copyright 2010-2024 Colorize
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.