java—如何使用gradle将javafx应用程序部署为可执行的jar或exe?

ct2axkht  于 2021-06-29  发布在  Java
关注(0)|答案(2)|浏览(669)

我想用javafx做一个应用程序。我刚接触javafx和gradle,所以现在我只是在胡闹。我想尝试的第一件事是将应用程序构建到可执行jar或exe。我试过好几种方法,但从来没有一个简单的jar来执行。当我双击它时它不会执行。当我试图从命令行执行它时,我得到了一个错误:错误:javafx运行时组件丢失,运行这个应用程序需要这些组件。我尝试了许多在web上找到的解决方案,但都没有成功。
这是应用程序的代码:

package org.example;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        Label label = new Label("hello world");
        Scene scene = new Scene(label);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

这是我的build.gradle文件:

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.9'
}

group 'org.example'
version ''

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    version = "15.0.1"
    modules = [ 'javafx.controls','javafx.fxml' ]
}
mainClassName = 'org.example.Main'
jar {
    manifest {
        attributes 'Main-Class': 'org.example.Main'
    }
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

有人知道怎么解决这个问题吗?

3vpjnl9f

3vpjnl9f1#

使用 jlink 构建包含javafx模块的java运行时。
使用 jpackage (来自jdk 14和更高版本)使用该运行时绑定应用程序。这将为您提供一个.exe启动程序和特定于平台的安装程序。
gradle'exec'任务可以作为构建的一部分轻松地为您运行这两个工具。
为了使事情更简单,您可以考虑使用包含javafx模块的jdk,这样就不必在配置javafxsdk和模块路径时乱来。有来自azul和bellsoft的openjdk构建,包括javafx。
下面是我在jpackage中使用的任务类型的一个示例。我的“appimage”任务复制文件以准备绑定,我的jlink任务创建运行时映像。这个任务创建了一个可运行的应用程序映像,我使用它第二次调用jpackage来生成安装程序。如果您不需要安装程序,也可以只压缩此映像,但每个映像都是特定于平台的,因为它包含本机启动器和要使用的jre:

task jpackageImage(type: Exec, dependsOn: [jlink, appImage]) {
    workingDir = project.projectDir
    inputs.property('consoleApp', project.consoleApp)
    inputs.property('vendorName', project.vendorName)
    if (project.hasProperty('copyright')) {
        inputs.property('copyright', project.copyright)
    }
    // TODO set input directory
    inputs.dir "${buildDir}${File.separator}image${File.separator}app"
    inputs.dir "${buildDir}${File.separator}image${File.separator}runtime"
    // define outputs
    outputs.dir "${buildDir}${File.separator}application"

    // in a doFirst in case values change (e.g. archive name gets version bump)
    // after configuration phase
    doFirst {
        // Error: Application output directory XXXXXXXXXXXXXX already exists.
        def tmpRoot = "$buildDir/tmp/image"
        project.delete tmpRoot
        project.delete "${buildDir}${File.separator}application"

        // resource directories need to exist
        project.mkdir resourceDir

        //file("${buildDir}/image/app/README.txt").text = "This file should be installed."

        def appName = project.applicationName // project.applicationName.replaceAll(" ","")
        def copyrightStr = project.hasProperty('copyright') ? project.copyright.toString() : "Copyright (c) ${year} ${vendorName}".toString().trim()
        def tmp = [
            "${jpackageTool}",
            '--type', 'app-image', // Valid values on Windows are: {"app-image", "exe", "msi"}
            '--verbose',
            '--temp', tmpRoot,
            '--app-version', project.version,
            '--input', "${buildDir}${File.separator}image${File.separator}app",
            '--runtime-image', "${buildDir}${File.separator}image${File.separator}runtime",
            '--name', appName,
            '--main-jar', "libs${File.separator}${configurations.runtime.artifacts.files.singleFile.name}",
            '--main-class', mainClass,
            '--resource-dir', resourceDir,
            '--icon', iconFileStr,
            '--description', project.description,
            '--vendor', vendorName,
            //'--category', 'Utility',
            '--copyright', copyrightStr,
            '--dest', "${buildDir}${File.separator}application",
            ]
        // Use a console app for easier debugging (log messages/debug prints are visible)
        if (osName.startsWith('windows')) {
            // Windows-specific options
            //'--win-menu',
            //'--win-menu-group', vendorName,
            //'--win-upgrade-uuid', project.upgradeUUID,
            //'--win-shortcut',
            // for a console application
            if (project.consoleApp) {
                tmp.addAll(['--win-console'])
            }
        }
        if (osName.startsWith('mac')) {
            tmp.addAll([
                // macOS-specific options
                //This name must be less than 16 characters long and be suitable for displaying in the menu bar and the application Info window.
                '--mac-package-name', project.macPkgName,
                '--mac-package-identifier', project.macPkgIndentifier,
                //'--mac-package-signing-prefix', <prefix string>,
                //'--mac-sign', // Request that the bundle be signed
                //'--mac-signing-keychain', <file path>,
                //'--mac-signing-key-user-name', '<team name>'
            ])
        }
        commandLine = tmp
        println commandLine
    }

    // workaround https://bugs.openjdk.java.net/browse/JDK-8254920
    doLast {
        if (osName.startsWith('windows') && jlinkCompression == 2) {
            project.copy {
                from "${buildDir}\\application\\${project.applicationName}\\runtime\\bin\\zip.dll"
                into "${buildDir}\\application\\${project.applicationName}"
            }
        }
    }
}

我真的应该在某个时候为这个东西做一个合适的gradle插件。
有关jpackage的更多信息,请访问https://docs.oracle.com/en/java/javase/14/docs/specs/man/jpackage.html

r6l8ljro

r6l8ljro2#

如果您希望对像您这样的示例进行一个短期的、有点不稳定的修复,那么可以在与当前类相同的类路径中使用main方法创建另一个类。在该方法的主体中,您将执行main.main(args)。这将诱使程序不认为它是java应用程序,从而使其正常工作。此外,还必须将主类名更改为新类的名称。
资料来源:https://edencoding.com/runtime-components-error/

相关问题