如何使用gradle将javafx应用程序部署为可执行jar或exe?
我想用javafx制作一个应用程序。我是JavaFX和gradle的新手,所以现在我只是在胡闹。我想尝试的第一件事是将应用程序构建到可执行jar或exe。我尝试过几种方法,但从未得到一个简单的jar来执行。当我双击它时,它不会执行。当我试图从命令行执行它时,我得到了一个错误:错误:缺少JavaFX运行时组件,运行这个应用程序需要这些组件 我在网上找到了很多解决方案,但都没有成功 这是应用程序的代码:如何使用gradle将javafx应用程序部署为可执行jar或exe?,java,gradle,javafx,exe,executable-jar,Java,Gradle,Javafx,Exe,Executable Jar,我想用javafx制作一个应用程序。我是JavaFX和gradle的新手,所以现在我只是在胡闹。我想尝试的第一件事是将应用程序构建到可执行jar或exe。我尝试过几种方法,但从未得到一个简单的jar来执行。当我双击它时,它不会执行。当我试图从命令行执行它时,我得到了一个错误:错误:缺少JavaFX运行时组件,运行这个应用程序需要这些组件 我在网上找到了很多解决方案,但都没有成功 这是应用程序的代码: package org.example; import javafx.application.
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) }
}
}
有人知道如何解决这个问题吗?如果你想为像你这样的例子提供一个短期的、有点不稳定的修复,你可以在与当前类相同的类路径中使用一个main方法创建另一个类。在该方法的主体中,您将执行Main.Main(args)。这将诱使程序不认为它是Java应用程序,从而使其正常工作。此外,还必须将主类名称更改为新类的名称
来源:使用
jlink
构建包含JavaFX模块的Java运行时
使用jpackage
(来自JDK 14和更高版本)使用该运行时绑定应用程序。这将为您提供一个.exe启动器和特定于平台的安装程序
作为构建的一部分,Gradle“Exec”任务可以轻松地为您运行这两个工具
为了使事情更容易,您可以考虑使用包含JavaFX模块的JDK,这样您就不必在配置JavaFX SDK和模块路径上乱七八糟。 有来自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}"
}
}
}
}
task jpackageImage(类型:Exec,dependsOn:[jlink,appImage]){
workingDir=project.projectDir
inputs.property('consoleApp',project.consoleApp)
inputs.property('vendorName',project.vendorName)
if(project.hasProperty(“版权”)){
输入。属性(“版权”,项目。版权)
}
//TODO设置输入目录
inputs.dir“${buildDir}${File.separator}image${File.separator}app”
inputs.dir“${buildDir}${File.separator}image${File.separator}运行时”
//定义输出
outputs.dir“${buildDir}${File.separator}应用程序”
//在doFirst中,如果值发生更改(例如,存档名称获得版本凹凸)
//配置阶段之后
首先{
//错误:应用程序输出目录XXXXXXXXXXXXX已存在。
def tmpRoot=“$buildDir/tmp/image”
project.delete tmpRoot
project.delete“${buildDir}${File.separator}应用程序”
//资源目录必须存在
project.mkdir资源目录
//文件(“${buildDir}/image/app/README.txt”).text=“应安装此文件。”
def appName=project.applicationName//project.applicationName.replaceAll(“,”)
def copyright str=project.hasProperty('copyright')?project.copyright.toString():“copyright(c)${year}${vendorName}”.toString().trim()
def tmp=[
“${jpackageTool}”,
“--type”、“app-image”,//Windows上的有效值为:{“app-image”、“exe”、“msi”}
“--冗长”,
“--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}”,
"主类","主类",,
“--资源目录”,资源目录,
“--图标”,图标列表,
“--description”,project.description,
“--供应商”,供应商名称,
//“--类别”“实用程序”,
“--版权”,版权STR,
'--dest',“${buildDir}${File.separator}应用程序”,
]
//使用控制台应用程序以简化调试(可以看到日志消息/调试打印)
if(osName.startsWith('windows')){
//Windows特定选项
//“--赢菜单”,
//“--赢得菜单组”,供应商名称,
//“--win upgrade uuid”,project.upgradeUUID,
//“--赢得捷径”,
//对于控制台应用程序
if(project.consoleApp){
tmp.addAll(['--win console']))
}
}
if(osName.startsWith('mac')){
tmp.addAll([
//macOS特定选项
//此名称的长度必须小于16个字符,并且适合显示在菜单栏和“应用程序信息”窗口中。
“--mac包名”,project.macPkgName,
“--mac包标识符”,project.macpkginIdentifier,
//“--mac包签名前缀”,
//'--mac sign',//请求对包进行签名
//“--mac签名密钥链”,
//'--mac签名密钥用户名',''
])
}
命令行=tmp
println命令行
}
//变通办法https://bugs.openjdk.java.net/browse/JDK-8254920
多拉斯特{
if(osName.startsWith('windows')&&jlinkCompression==2){
project.copy{
来自“${buildDir}\\application\\${project.applicationName}\\runtime\\bin\\zip.dll”