FWIW what I'm doing to build a windows app for jpackager, in terms of
gradle tasks, that isn't modular. I hope this cleans up over time, but
this is the final result of having just got the damn thing to work! :-)
I think *eventually* we'll have a single call to jpackager do the whole
lot. But I think we're not there yet, so it's split out into separate
steps. You *will* need to make changes for your own context:
Build the JRE needed using JLink, supplying the needed modules. The
JLink task referenced is actually written in Java and wraps
ToolProvider, but it's pretty trivial and could almost-more-easily be
done with an Exec. NB: The JLink task as written puts it in a "java"
subdirectory of the given destinationDir.
task buildAdminJre(type: JLink) {
description 'Build the Client JRE for ' + nativeOsName
destinationDir
rootProject.file("deploy/bindist/"+requiredJava.merusNativeAdminJreName)
modules = [
'java.base',
'java.desktop',
'java.xml',
'java.logging'
]
bindServices false
modulePath =
[System.properties.getProperty('java.home')+File.separatorChar+'jmods']
noHeaderFiles true
noManPages true
stripDebug true
}
Have the jar task build the application as normal. Here's mine as an
example. The important part is, you don't need to build a single fat
jar, but you can include the dependent jars with the Class-Path line
below. Mine isn't a JavaFX app, so I don't know what it does now wrt
pulling in the openjfx jars here, or whether you add them as modules to
the jlink task above. I expect the former would be preferable.
/*
Basic non-fat jar.
*/
jar {
manifest {
attributes(
'Product-Name': applicationName,
'Main-Class': mainClassName,
'Package-Title': project.group,
'Package-Vendor': vendor,
'Package-Version': adminVersion,
'Permissions': "all-permissions",
'Class-Path':
configurations.runtimeClasspath.files.collect { it.getName() }.join(' ')
)
}
archiveName = 'adm.jar'
}
Build the application image. This should be mostly platform independent.
(I think the only thing stopping it being is probably the .ico file for
the icon.) Note I'm supplying as --input the lib dir from the target
built by the standard gradle installDist task, so it contains the
dependencies as well. adm.jar's manifest lists these as dependencies in
Class-Path as per above. The JRE is supplied in the --runtime-image
parameter.
task createImage(type: Exec) {
description 'Build the App Image for this platform using jpackager'
dependsOn installDist
dependsOn buildAdminJre
def imageDir = "$buildDir/image"
outputs.dir new File(imageDir,applicationName)
commandLine 'jpackager', 'create-image',
'--verbose',
'--version',adminVersion,
'--input', new
File(installDist.outputs.files.singleFile,"lib"),
'--output',imageDir,
'--name',applicationName,
'--description','<DESCRIPTION>',
'--main-jar','adm.jar',
'--class',mainClassName,
'--icon','src/main/files/app-icon.ico',
'--runtime-image',new
File(buildAdminJre.outputs.files.singleFile,"java"),
'--vendor',vendor
}
You'll now have the application image suitable for your platform. I've
only tried this on Windows so far.
Note: My app has a space in the applicationName value. This breaks at
the moment. The following is a workaround task, split out for easy
removal later when I'm sure it won't be necessary any more:
task fixWindowsImage(type: Copy) {
/*
This task creates a copy of the image created by createImage task
and fixes it up for using as the source of a Windows Installer.
As of first writing, what this means is renaming a couple of files,
because create-image crushes out spaces in the application name but
we want them in, we have to rename the generated .exe and .cfg
files
to have that space again.
Then msiInstaller will work to actually create the shortcut and
start menu items.
*/
dependsOn createImage
from createImage.outputs.files.singleFile
into "$buildDir/fixedImage/$applicationName"
rename ("<NAME-WITHOUT-SPACES>.exe", applicationName+".exe")
rename ("<NAME-WITHOUT-SPACES>.cfg", applicationName+".cfg")
}
Then, as a separate stage, run the bit that puts it into an installer.
So here the input supplied in --app-image is the complete application
image as already created and fixed by the above tasks.
task msiInstaller(type: Exec) {
/*
see fixWindowsImage for a necessary fix to the created application
image to allow shortcut and startmenu items to work.
*/
description 'Build the Windows 64-bit MSI installer using
jpackager'
doFirst {
/*
For no reason I can discern, this task stopped working
(did nothing, as if not called) until I added this
doFirst{} block
*/
println description
}
dependsOn fixWindowsImage
def msiDir = "$buildDir/msi"
def msiName = applicationName+'-'+adminVersion+'.msi'
outputs.file new File(msiDir, msiName)
commandLine 'jpackager', 'create-installer', 'msi',
'--verbose',
'--version',adminVersion,
'--app-image',fixWindowsImage.outputs.files.singleFile,
'--output', msiDir,
'--name',applicationName,
'--description','<DESCRIPTION>',
'--icon','src/main/files/app-icon.ico',
'--win-per-user-install',
'--win-shortcut',
'--win-menu',
'--win-menu-group','<SUBMENU-NAME>',
'--win-upgrade-uuid','<UUID>'
}
Left for your own amusement, code-signing! (Off-Topic here although
arguably jpackager could include that too.)
--
Rachel
Post by Lennart BörjesonI've been trying to understand how to use the jpackager, but I'm stumped.
I have for a long been using the now defunct gradle-javafx plugin, so I've never really used the old javapackager either, only indirectly through gradle.
Let's say I have a non-modular application packaged as an executable jar, i.e. a GUI I can launch with the "java -jar" command: How can I package this as a native application/dmg with jpackager?
On a more general note: What's the required layout/contents of the "input directory"?
Best regards,
/Lennart Börjeson