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

On 09/11/2018 14:04, Lennart Börjeson wrote:
I'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.

So I'm maybe asking terribly noob questions, but here goes:

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

Reply via email to