使用 Gradle 将依赖项添加到运行时映像
Add dependecies to runtime image with Gradle
我不知道如何添加依赖。我的模块需要 Log4j。我在模块信息中添加了要求。我还添加了 gradle 依赖项。我可以 运行 项目,但无法创建自定义 运行 时间图像。
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.5'
}
group 'eu.sample'
version '2.0'
repositories {
mavenCentral()
}
javafx {
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
mainClassName = "$moduleName/eu.sample.app.Main"
def lin_java_home = hasProperty('org.gradle.java.home') ? getProperty('org.gradle.java.home') : System.getenv('JAVA_HOME')
def lin_fx_jmods = hasProperty('linux.fx.mods') ? getProperty('linux.fx.mods') : System.getenv('PATH_TO_FX_MODS_LIN')
def win_java_home = hasProperty('windows.java.home') ? getProperty('windows.java.home') : System.getenv('JAVA_HOME_WIN')
def win_fx_jmods = hasProperty('windows.fx.mods') ? getProperty('windows.fx.mods') : System.getenv('PATH_TO_FX_MODS_WIN')
dependencies {
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.1'
}
task jlink(type: Exec) {
dependsOn 'clean'
dependsOn 'jar'
workingDir 'build'
if (lin_java_home == null) {
throw new RuntimeException("java_home is not defined.")
}
if (lin_fx_jmods == null) {
throw new RuntimeException("fx_jmods is not defined.")
}
commandLine "${lin_java_home}/bin/jlink", '--module-path', "libs${File.pathSeparatorChar}${lin_fx_jmods}",
'--add-modules', "${moduleName}", '--output', "${moduleName}", '--strip-debug',
'--compress', '2', '--no-header-files', '--no-man-pages'
}
task jlinkWin(type: Exec) {
dependsOn 'clean'
dependsOn 'jar'
workingDir 'build'
if (win_java_home == null) {
throw new RuntimeException("java_home is not defined.")
}
if (win_fx_jmods == null) {
throw new RuntimeException("fx_jmods is not defined.")
}
commandLine "${lin_java_home}/bin/jlink", '--module-path',
"${win_java_home}/jmods${File.pathSeparatorChar}libs${File.pathSeparatorChar}${win_fx_jmods}",
'--add-modules', "${moduleName}", '--output', "${moduleName}", '--strip-debug',
'--compress', '2', '--no-header-files', '--no-man-pages'
}
当我启动任务 jlink 时,我得到:
Error: Module org.apache.logging.log4j not found, required by
app
我检查了构建中的 libs 目录,没有 log4j jar。如何告诉gradle给jlink任务添加依赖?
问题
这是您在 jlink
任务中的内容:
'--module-path', "libs${File.pathSeparatorChar}${fx_jmods}"
Whats 意味着您正在添加依赖项:
libs
:基本上是你的模块 helloFX
的 jar。这是 jar
任务的结果,该任务仅包括项目的 classes 和资源,但不包括其依赖项。
fx_jmods
:这是JavaFX jmods的路径。
但是当你 运行 时,你会得到这个错误:
Error: Module org.apache.logging.log4j not found, required by app
该错误表示 jlink
命令的 module-path
不完整,无法解析所有必需的依赖项。如上所述,我们只包含 module.jar 和 JavaFX (jmods) jar,而不包含 log4j.jar
.
所以我们需要找到一种方法将该 jar 添加到模块路径中。
有几种可能的解决方案可以在您的自定义图像中包含第三方依赖项。
解决方案 1
我们必须修改 jlink
任务,以在我们的 运行 时间配置中包含现有的依赖项。
最直接的解决方案是在本地 .gradle 存储库中找到 gradle 存储 logj4 jar 的位置。
'--module-path', "libs${File.pathSeparatorChar}${fx_jmods}: \
/Users/<user>/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.1/268..a10/log4j-api-2.11.1.jar: \
/Users/<user>/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.1/59..e4/log4j-core-2.11.1.jar"
虽然此解决方案有效,但它当然不是最方便的,因为它取决于用户的本地路径。
解决方案 2
一个更好的解决方案,可以添加一个任务来复制 运行 时间依赖项,这些依赖项由 gradle 直接解析到 libs
文件夹中,例如:
task libs(type: Copy) {
into 'build/libs/'
from configurations.runtime
}
然后从 jlink
任务中调用此任务:
task jlink(type: Exec) {
dependsOn 'clean'
dependsOn 'jar'
dependsOn 'libs'
...
}
如果您 运行 ./gradlew jlink
并检查 libs
文件夹,您应该会找到如下内容:
build/libs/hellofx.jar
build/libs/javafx-base-11.0.1.jar
build/libs/javafx-base-11.0.1-$platform.jar
build/libs/javafx-graphics-11.0.1.jar
build/libs/javafx-graphics-11.0.1-$platform.jar
build/libs/javafx-controls-11.0.1.jar
build/libs/javafx-controls-11.0.1-$platform.jar
build/libs/javafx-fxml-11.0.1.jar
build/libs/javafx-fxml-11.0.1-$platform.jar
build/libs/log4j-api-2.11.1.jar
build/libs/log4j-core-2.11.1.jar
其中 $platform
是您的 运行ning 平台。
请注意 libs
文件夹现在包含 所有 模块路径所需的依赖项,以及 JavaFX-*-$platform jar 包含 本机库,因此,模块路径选项中不再需要 jmod。这就足够了:
'--module-path', "libs"
因此您的命令行将是:
commandLine "${java_home}/bin/jlink", '--module-path', "libs",
'--add-modules', "${moduleName}", '--output', "${moduleName}", '--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'
您现在可以 运行 您的 jlink
任务成功了。
解决方案 3
根据@madhead 评论的建议,您可以为 jlink
任务使用另一个插件:所谓的 badass-jlink-plugin.
将您的构建修改为:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.5'
id 'org.beryx.jlink' version '2.1.8'
}
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.1'
}
javafx {
modules = ['javafx.controls', 'javafx.fxml']
}
mainClassName = "${moduleName}/eu.sample.app.Main"
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'helloFX'
}
}
另请注意,该插件具有为其他平台创建图像的选项(参见 targetPlatform)。
编辑 log4j
正如评论中提到的,log4j
依赖性还不能很好地与模块一起使用。 Apache 问题跟踪器上有一个 open issue:
Currently based on the given automatic modules you can't use log4j-core in a project where you like to use jlink to produce a runtime image.
我已将此添加到我的主 class:
LogManager.getLogger(MainApp.class).info("hellofx!");
并且我已将 log4j2.xml
文件添加到 src/main/resources
。
运行./gradlew run
,作品:
> Task :run
18:24:26.362 [JavaFX Application Thread] INFO eu.sample.app.Main - hellofx!
但是,解决方案 2 中的 运行ning jlink 创建了自定义图像,我可以验证是否包含 xml 文件,但是当 运行ning 从我得到的图像中:
build/hellofx/bin/java -m hellofx/eu.sample.app.Main
ERROR StatusLogger Log4j2 could not find a logging implementation. \
Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
如前所述,运行使用解决方案 3 中的插件使用 jlink 在 createMergedModule
任务中失败:
error: package org.apache.logging.log4j.spi is not visible
provides org.apache.logging.log4j.spi.Provider with org.apache.logging.log4j.core.impl.Log4jProvider;
[参见 EDIT(2),自版本 2.1.9 以来已修复]
备选
在这一点上,一个可能的替代方案是使用 Slf4j。插件文档中列出了一个成功使用它的 sample。
总而言之,这是必需的:
Gradle 文件:
dependencies {
compile 'org.slf4j:slf4j-api:1.8.0-beta2'
compile('ch.qos.logback:logback-classic:1.3.0-alpha4') {
exclude module: "activation"
}
}
模块信息:
requires org.slf4j;
requires ch.qos.logback.classic;
requires java.naming;
主要class:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(MainApp.class);
...
logger.info("hellofx!");
logging.properties
来自 here and logback.xml
from here,你的主要 class.
运行ning ./gradlew
运行 或 ./gradlew jlink
和 build/image/bin/HelloFX
都工作,并且消息记录到控制台。
编辑 (2)
在 badass-jlink-plugin 的问题跟踪器 reporting 问题之后,这个问题已经解决,从 2.1.19 版本开始,它应该可以正常创建自定义图像。
由于 log4j
是多版本 jar,所以需要做一件事:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.5'
id 'org.beryx.jlink' version '2.1.9'
}
...
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'helloFX'
}
forceMerge('log4j-api') // <---- this is required for Log4j
}
查看完整的工作示例 here。
我不知道如何添加依赖。我的模块需要 Log4j。我在模块信息中添加了要求。我还添加了 gradle 依赖项。我可以 运行 项目,但无法创建自定义 运行 时间图像。
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.5'
}
group 'eu.sample'
version '2.0'
repositories {
mavenCentral()
}
javafx {
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
mainClassName = "$moduleName/eu.sample.app.Main"
def lin_java_home = hasProperty('org.gradle.java.home') ? getProperty('org.gradle.java.home') : System.getenv('JAVA_HOME')
def lin_fx_jmods = hasProperty('linux.fx.mods') ? getProperty('linux.fx.mods') : System.getenv('PATH_TO_FX_MODS_LIN')
def win_java_home = hasProperty('windows.java.home') ? getProperty('windows.java.home') : System.getenv('JAVA_HOME_WIN')
def win_fx_jmods = hasProperty('windows.fx.mods') ? getProperty('windows.fx.mods') : System.getenv('PATH_TO_FX_MODS_WIN')
dependencies {
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.1'
}
task jlink(type: Exec) {
dependsOn 'clean'
dependsOn 'jar'
workingDir 'build'
if (lin_java_home == null) {
throw new RuntimeException("java_home is not defined.")
}
if (lin_fx_jmods == null) {
throw new RuntimeException("fx_jmods is not defined.")
}
commandLine "${lin_java_home}/bin/jlink", '--module-path', "libs${File.pathSeparatorChar}${lin_fx_jmods}",
'--add-modules', "${moduleName}", '--output', "${moduleName}", '--strip-debug',
'--compress', '2', '--no-header-files', '--no-man-pages'
}
task jlinkWin(type: Exec) {
dependsOn 'clean'
dependsOn 'jar'
workingDir 'build'
if (win_java_home == null) {
throw new RuntimeException("java_home is not defined.")
}
if (win_fx_jmods == null) {
throw new RuntimeException("fx_jmods is not defined.")
}
commandLine "${lin_java_home}/bin/jlink", '--module-path',
"${win_java_home}/jmods${File.pathSeparatorChar}libs${File.pathSeparatorChar}${win_fx_jmods}",
'--add-modules', "${moduleName}", '--output', "${moduleName}", '--strip-debug',
'--compress', '2', '--no-header-files', '--no-man-pages'
}
当我启动任务 jlink 时,我得到:
Error: Module org.apache.logging.log4j not found, required by app
我检查了构建中的 libs 目录,没有 log4j jar。如何告诉gradle给jlink任务添加依赖?
问题
这是您在 jlink
任务中的内容:
'--module-path', "libs${File.pathSeparatorChar}${fx_jmods}"
Whats 意味着您正在添加依赖项:
libs
:基本上是你的模块helloFX
的 jar。这是jar
任务的结果,该任务仅包括项目的 classes 和资源,但不包括其依赖项。fx_jmods
:这是JavaFX jmods的路径。
但是当你 运行 时,你会得到这个错误:
Error: Module org.apache.logging.log4j not found, required by app
该错误表示 jlink
命令的 module-path
不完整,无法解析所有必需的依赖项。如上所述,我们只包含 module.jar 和 JavaFX (jmods) jar,而不包含 log4j.jar
.
所以我们需要找到一种方法将该 jar 添加到模块路径中。
有几种可能的解决方案可以在您的自定义图像中包含第三方依赖项。
解决方案 1
我们必须修改 jlink
任务,以在我们的 运行 时间配置中包含现有的依赖项。
最直接的解决方案是在本地 .gradle 存储库中找到 gradle 存储 logj4 jar 的位置。
'--module-path', "libs${File.pathSeparatorChar}${fx_jmods}: \
/Users/<user>/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.11.1/268..a10/log4j-api-2.11.1.jar: \
/Users/<user>/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.11.1/59..e4/log4j-core-2.11.1.jar"
虽然此解决方案有效,但它当然不是最方便的,因为它取决于用户的本地路径。
解决方案 2
一个更好的解决方案,可以添加一个任务来复制 运行 时间依赖项,这些依赖项由 gradle 直接解析到 libs
文件夹中,例如:
task libs(type: Copy) {
into 'build/libs/'
from configurations.runtime
}
然后从 jlink
任务中调用此任务:
task jlink(type: Exec) {
dependsOn 'clean'
dependsOn 'jar'
dependsOn 'libs'
...
}
如果您 运行 ./gradlew jlink
并检查 libs
文件夹,您应该会找到如下内容:
build/libs/hellofx.jar
build/libs/javafx-base-11.0.1.jar
build/libs/javafx-base-11.0.1-$platform.jar
build/libs/javafx-graphics-11.0.1.jar
build/libs/javafx-graphics-11.0.1-$platform.jar
build/libs/javafx-controls-11.0.1.jar
build/libs/javafx-controls-11.0.1-$platform.jar
build/libs/javafx-fxml-11.0.1.jar
build/libs/javafx-fxml-11.0.1-$platform.jar
build/libs/log4j-api-2.11.1.jar
build/libs/log4j-core-2.11.1.jar
其中 $platform
是您的 运行ning 平台。
请注意 libs
文件夹现在包含 所有 模块路径所需的依赖项,以及 JavaFX-*-$platform jar 包含 本机库,因此,模块路径选项中不再需要 jmod。这就足够了:
'--module-path', "libs"
因此您的命令行将是:
commandLine "${java_home}/bin/jlink", '--module-path', "libs",
'--add-modules', "${moduleName}", '--output', "${moduleName}", '--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'
您现在可以 运行 您的 jlink
任务成功了。
解决方案 3
根据@madhead 评论的建议,您可以为 jlink
任务使用另一个插件:所谓的 badass-jlink-plugin.
将您的构建修改为:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.5'
id 'org.beryx.jlink' version '2.1.8'
}
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.1'
}
javafx {
modules = ['javafx.controls', 'javafx.fxml']
}
mainClassName = "${moduleName}/eu.sample.app.Main"
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'helloFX'
}
}
另请注意,该插件具有为其他平台创建图像的选项(参见 targetPlatform)。
编辑 log4j
正如评论中提到的,log4j
依赖性还不能很好地与模块一起使用。 Apache 问题跟踪器上有一个 open issue:
Currently based on the given automatic modules you can't use log4j-core in a project where you like to use jlink to produce a runtime image.
我已将此添加到我的主 class:
LogManager.getLogger(MainApp.class).info("hellofx!");
并且我已将 log4j2.xml
文件添加到 src/main/resources
。
运行./gradlew run
,作品:
> Task :run
18:24:26.362 [JavaFX Application Thread] INFO eu.sample.app.Main - hellofx!
但是,解决方案 2 中的 运行ning jlink 创建了自定义图像,我可以验证是否包含 xml 文件,但是当 运行ning 从我得到的图像中:
build/hellofx/bin/java -m hellofx/eu.sample.app.Main
ERROR StatusLogger Log4j2 could not find a logging implementation. \
Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
如前所述,运行使用解决方案 3 中的插件使用 jlink 在 :createMergedModule
任务中失败
error: package org.apache.logging.log4j.spi is not visible
provides org.apache.logging.log4j.spi.Provider with org.apache.logging.log4j.core.impl.Log4jProvider;
[参见 EDIT(2),自版本 2.1.9 以来已修复]
备选
在这一点上,一个可能的替代方案是使用 Slf4j。插件文档中列出了一个成功使用它的 sample。
总而言之,这是必需的:
Gradle 文件:
dependencies {
compile 'org.slf4j:slf4j-api:1.8.0-beta2'
compile('ch.qos.logback:logback-classic:1.3.0-alpha4') {
exclude module: "activation"
}
}
模块信息:
requires org.slf4j;
requires ch.qos.logback.classic;
requires java.naming;
主要class:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(MainApp.class);
...
logger.info("hellofx!");
logging.properties
来自 here and logback.xml
from here,你的主要 class.
运行ning ./gradlew
运行 或 ./gradlew jlink
和 build/image/bin/HelloFX
都工作,并且消息记录到控制台。
编辑 (2)
在 badass-jlink-plugin 的问题跟踪器 reporting 问题之后,这个问题已经解决,从 2.1.19 版本开始,它应该可以正常创建自定义图像。
由于 log4j
是多版本 jar,所以需要做一件事:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.5'
id 'org.beryx.jlink' version '2.1.9'
}
...
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'helloFX'
}
forceMerge('log4j-api') // <---- this is required for Log4j
}
查看完整的工作示例 here。