Where do I find JavaFX ant tasks in Java 11?

425 views Asked by At

VSCode, Java 11 JavaFX 18.0.2

I am trying to package my code up for distribution as a desktop app. In my case I want a fully self-contained app because of my target user's profile.

I have been through Jenkov add the Oracle docs here and here which suggest I need ant-javafx.jar. That jar file seems to have been dropped from the standard Java SDK some time around Java 7 and put into the regular JavaFX install lib folder.

It's not there in the build I have.

JavaFX seems to have gone to openjfx.io and nowhere in there can I see support for the ant packaging jar. In fact I see openjfx as a retrograde step as they are increasingly forcing everyone into paid plans (try going round and round the loop of downloading anything that doesn't require an LTS payment).

I have a suspicion that there is some silent assumption that everyone will use something from maven or gradle, and maybe the packaging tools are buried away in one of those build tools. For historical reasons I don't use either and it should be possible to do this packaging without one of them.

So where do I get the JavaFX Ant build tasks from without having to pay someone?

2

There are 2 answers

0
guymac On

I have found that the following works as an alternative with Java 19 and OpenJFX 19. I use the maven-dependency-plugin to copy all the dependency jars (excluding JavaFX, which I use as modules from a "full" JDK [one that includes JavaFX)] into the target/lib directory.

#!/bin/bash

set -o errexit
set -o noclobber            
set -o xtrace

# find dependency modules of required modules
DEP_MODS=$(jdeps -quiet --class-path "target/lib/*"  --add-modules java.base,java.logging,java.sql,javafx.controls,javafx.fxml --multi-release base --ignore-missing-deps --print-module-deps target/myapp-4.0-beta.jar)

# create a modular runtime image
jlink --compress=1 --no-header-files --no-man-pages --add-modules "java.logging,java.sql,javafx.controls,javafx.fxml,$DEP_MODS" --output target/myapp-4.0-beta

# Example of running it out of the runtime image
# TEST target/myapp-4.0-beta/bin/java -cp "../../myapp-4.0-beta.jar:../../lib/*" org.myapp.App

# symlink to the artifact jar from the lib directory
$(cd target/lib && ln -s ../myapp-4.0-beta.jar)

# use the lib directory and modular runtime image as input to jpackage
jpackage --input target/lib --runtime-image target/myapp-4.0-beta --main-jar myapp-4.0-beta.jar --main-class org.myapp.App  --type app-image --app-version 4.0 --name app --dest target/dist/bundle --mac-entitlements src/dist/mac/entitlements.plist
0
Francis Fajardo On

It is still possible to invoke the new jlink and jpackage tools from an Ant build file without having to depend on any 3rd party plugins (e.g., Maven, Gradle). I've recently had the need for them in a recent JavaFX project and they're available in this repository.

Here's the build file from that repository (as of writing):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project name="javafx-build" default="do-deploy">
    <!-- Application Details -->
    <property name="app.vendor" value="Vendor Name"/>
    <property name="app.title" value="App Name"/>
    <property name="app.version" value="0.0.0"/>
    <property name="app.mainClass" value="app.Main"/>
    <property name="app.outJar" value="out.jar"/>

    <!-- Packaging -->
    <property name="app.package.license" value="LICENSE.txt"/>
    <property name="app.package.icon" value="ICON.ico"/>
    <property name="app.package.flags.win" value="--win-dir-chooser --win-menu --win-per-user-install --win-shortcut"/>
    <property name="app.package.type.win" value="msi"/>
    <property name="app.package.type.unix" value=""/>
    <property name="app.package.type.mac" value=""/>

    <!-- Directories -->
    <property name="dir.sdk.javafx" value="C:\dev\java\jfx20"/>
    <property name="dir.bin" value="../bin"/>
    <property name="dir.dist" value="dist"/>
    <property name="dir.dist.jar" value="${dir.dist}/jar"/>
    <property name="dir.dist.jmods" value="${dir.dist}/jmods"/>
    <property name="dir.dist.runtime" value="${dir.dist}/rt-image"/>
    <property name="dir.dist.package" value="${dir.dist}/package"/>

    <!-- Conditional properties -->
    <condition property="app.package.license.arg"
               value=""
               else="--license-file ${app.package.license}">
        <or>
            <equals arg1="${app.package.license}" arg2=""/>
            <not>
                <isset property="app.package.license"/>
            </not>
       </or>
    </condition>
    <condition property="app.package.icon.arg"
               value=""
               else="--icon ${app.package.icon}">
        <or>
            <equals arg1="${app.package.icon}" arg2=""/>
            <not>
                <isset property="app.package.icon"/>
            </not>
        </or>
    </condition>
    <condition property="app.package.flags.win.arg"
               value=""
               else="${app.package.flags.win}">
        <or>
            <equals arg1="${app.package.flags.win}" arg2=""/>
            <not>
                <isset property="app.package.flags.win"/>
            </not>
            <not>
                <os family="windows"/>
            </not>
        </or>
    </condition>
    <condition property="app.package.type.arg"
               value="--type ${app.package.type.win}">
        <and>
            <not>
                <equals arg1="${app.package.type.win}" arg2=""/>
            </not>
            <os family="windows"/>
       </and>
    </condition>
    <condition property="app.package.type.arg"
               value="--type ${app.package.type.unix}">
        <and>
            <not>
                <equals arg1="${app.package.type.unix}" arg2=""/>
            </not>
            <os family="unix"/>
       </and>
    </condition>
    <condition property="app.package.type.arg"
               value="--type ${app.package.type.mac}">
        <and>
            <not>
                <equals arg1="${app.package.type.mac}" arg2=""/>
            </not>
            <os family="mac"/>
       </and>
    </condition>
    <!-- Fallback -->
    <property name="app.package.type.arg" value=""/>

    <!-- Targets -->
    <target name="do-jar">
        <delete dir="${dir.dist.jar}"/>
        <jar destfile="${dir.dist.jar}/${app.outJar}">
            <manifest>
                <attribute name="Main-Class" value="${app.mainClass}"/>
                <attribute name="Class-Path" value="."/>
                <attribute name="Implementation-Vendor" value="${app.vendor}"/>
                <attribute name="Implementation-Title" value="${app.title}"/>
                <attribute name="Implementation-Version" value="${app.version}"/>
            </manifest>
            <fileset dir="${dir.bin}"/>
        </jar>
    </target>
    <target name="do-staging">
        <copy todir="${dir.dist.jmods}">
            <fileset dir="${dir.sdk.javafx}/jmods">
                <include name="*"/>
            </fileset>
        </copy>
        <delete dir="${dir.dist.runtime}"/>
        <exec executable="jlink" failonerror="true">
            <arg line="--strip-native-commands"/>
            <arg line="--no-header-files"/>
            <arg line="--no-man-pages"/>
            <arg line="--compress=2"/>
            <arg line="--strip-debug"/>
            <arg line="--module-path ${dir.dist.jmods}"/>
            <arg line="--add-modules ALL-MODULE-PATH"/>
            <arg line="--output ${dir.dist.runtime}"/>
        </exec>
    </target>
    <target name="do-package">
        <delete dir="${dir.dist.package}"/>
        <exec executable="jpackage" failonerror="true">
            <arg line="${app.package.type.arg}"/>
            <arg line="--dest ${dir.dist.package}"/>
            <arg line="--input ${dir.dist.jar}"/>
            <arg line="--main-class ${app.mainClass}"/>
            <arg line="--main-jar ${app.outJar}"/>
            <arg line="--runtime-image ${dir.dist.runtime}"/>
            <arg line="--name &quot;${app.title}&quot;"/>
            <arg line="--app-version &quot;${app.version}&quot;"/>
            <arg line="--vendor &quot;${app.vendor}&quot;"/>
            <arg line="${app.package.flags.win.arg}"/>
            <arg line="${app.package.license.arg}"/>
            <arg line="${app.package.icon.arg}"/>
        </exec>
    </target>
    <target name="do-deploy" depends="do-jar, do-staging, do-package">
    </target>
</project>

A few things to note: this expects that the program's compiled code (*.class) is in the ../bin directory and uses JavaFX jmods, assuming that they're in the directory you've specified for the dir.sdk.javafx property (but you're free to modify all of this to your liking).