问题描述
我不知道如何添加依赖关系.我的模块需要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' }
当我解雇任务时,我会得到:
错误:模块org.apache.logging.log4j找不到,要求 应用
我在构建中检查了Libs目录,并且没有Log4J Jar.如何告诉Gradle将依赖项添加到JLINK任务?
推荐答案
问题
这就是您在jlink任务中所拥有的:
'--module-path', "libs${File.pathSeparatorChar}${fx_jmods}"
是什么意味着您正在添加依赖项:
-
libs:基本上是模块的罐子helloFX.这是jar任务的结果,仅包括项目的类和资源,而不包括其依赖项.
-
fx_jmods:这是通往Javafx Jmods的途径.
但是,当您运行时,您会收到此错误:
错误:未找到的模块org.apache.logging.log4j,App
要求
错误意味着module-path命令的module-path尚未完成,并且无法解决所有所需的依赖项.如上所述,我们仅包括模块.
因此,我们需要找到一种将该罐子添加到模块路径的方法.
有一些可能的解决方案可以在您的自定义图像中包括第三方依赖.
解决方案1
我们必须修改jlink任务,以在运行时配置中包括现有依赖项.
最直接的解决方案是在local .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是您的跑步平台.
请注意,libs文件夹现在包含 all 模块路径所需的依赖项,以及Javafx - * - $ platform jars jars 包含因此,本地库在模块路径选项中不再需要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任务: so so 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 在Apache 问题跟踪器:
当前基于给定的自动模块,您无法在项目中使用log4j核心,您想使用jlink生成运行时图像.
我已经将其添加到我的主要课程中:
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运行jlink,创建自定义映像,我可以验证XML文件包括在内,但是当从图像中运行时,我得到了:
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 task> task 上失败:
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),此
自版本以来已修复.替代
在这一点上,可以改用SLF4J的替代方法.有一个示例从插件文档中列出的样本成功使用它的插件文档.
总而言之,这是必需的:
gradle文件:
dependencies { compile 'org.slf4j:slf4j-api:1.8.0-beta2' compile('ch.qos.logback:logback-classic:1.3.0-alpha4') { exclude module: "activation" } }
模块Info:
requires org.slf4j; requires ch.qos.logback.classic; requires java.naming;
主类:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static final Logger logger = LoggerFactory.getLogger(MainApp.class); ... logger.info("hellofx!");
logging.properties来自在这里和logback.xml来自在这里,与您的主要类别.
运行./gradlew运行或./gradlew jlink和build/image/bin/HelloFX工作,并且该消息已记录到控制台.
编辑(2)
之后报告问题跟踪器已解决,并且由于版本2.1.19,它应该可以很好地创建自定义图像.
由于log4j是多释放罐子,因此需要一件事:
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 }
请参阅一个完整的样本在这里P>
问题描述
I don't know how to add dependency. My module needs Log4j. I added requirement to module info. I added also to gradle dependencies. I can run project, but I can't create custom runtime image.
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' }
When I fire task jlink I get:
Error: Module org.apache.logging.log4j not found, required by app
I checked libs directory in build and there is not log4j jar. How to tell gradle to add dependencies to jlink task?
推荐答案
Problem
This is what you have in your jlink task:
'--module-path', "libs${File.pathSeparatorChar}${fx_jmods}"
Whats means that you are adding the dependencies from:
libs: basically the jar of your module helloFX. This is a result of the jar task, that only includes the classes and resources of your project, but not its dependencies.
fx_jmods: That is the path to the JavaFX jmods.
But when you run, you get this error:
Error: Module org.apache.logging.log4j not found, required by app
The error means that the module-path for the jlink command is not complete, and it can't resolve all the required dependencies. As mentioned above, we are only including the module.jar and the JavaFX (jmods) jars, but not the log4j.jar.
So we need to find a way to add that jar to the module-path.
There are a few possible solutions to include third party dependencies in your custom image.
Solution 1
We have to modify the jlink task, to include the existing dependencies in our runtime configuration.
The most immediate solution is finding where the logj4 jars are stored by gradle, in the local .gradle repository.
'--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"
While this solution works, it is not the most convenient, of course, as it depends on local paths from the user.
Solution 2
A better solution, can be done adding a task to copy the runtime dependencies, that are resolved directly by gradle, into the libs folder like:
task libs(type: Copy) { into 'build/libs/' from configurations.runtime }
and then calling this task from the jlink task:
task jlink(type: Exec) { dependsOn 'clean' dependsOn 'jar' dependsOn 'libs' ... }
If you run ./gradlew jlink and check the libs folder you should find something like this:
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
where $platform is your running platform.
Notice that the libs folder now contains all the dependencies that are required for the module path, and also that the JavaFX-*-$platform jars contain the native libraries, therefore, the jmods are not needed anymore in the module-path option. This will be enough:
'--module-path', "libs"
so your command line will be:
commandLine "${java_home}/bin/jlink", '--module-path', "libs", '--add-modules', "${moduleName}", '--output', "${moduleName}", '--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'
You can run your jlink task now successfully.
Solution 3
As suggested by @madhead comment, you can use another plugin for the jlink task: the so called badass-jlink-plugin.
Modify your build to something like:
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' } }
Note also that the plugin has an option to create image for other platforms (see targetPlatform).
EDIT about log4j
As mentioned in the comments, the log4j dependency doesn't play well with modules yet. There is an open issue at the Apache Issue tracker:
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.
I've added this to my main class:
LogManager.getLogger(MainApp.class).info("hellofx!");
and I've added the log4j2.xml file to src/main/resources.
Running ./gradlew run, works:
> Task :run 18:24:26.362 [JavaFX Application Thread] INFO eu.sample.app.Main - hellofx!
But, running jlink from Solution 2, creates the custom image, I can verify that the xml file is included, but when running from the image I get:
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...
And as mentioned, running jlink with the plugin from Solution 3 fails at the createMergedModule task:
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;
[See EDIT(2), this has been fixed since version 2.1.9]
Alternative
At this point, a possible alternative could be use Slf4j instead. There is a sample listed from the plugin documentation that uses it successfully.
In summary this is required:
Gradle file:
dependencies { compile 'org.slf4j:slf4j-api:1.8.0-beta2' compile('ch.qos.logback:logback-classic:1.3.0-alpha4') { exclude module: "activation" } }
Module-info:
requires org.slf4j; requires ch.qos.logback.classic; requires java.naming;
Main class:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static final Logger logger = LoggerFactory.getLogger(MainApp.class); ... logger.info("hellofx!");
logging.properties from here and logback.xml from here, with your main class.
Both running ./gradlew run or ./gradlew jlink, and build/image/bin/HelloFX work, and the message is logged to the console.
EDIT (2)
After reporting the issue to the badass-jlink-plugin's issue tracker, this was solved, and since version 2.1.19 it should work fine creating a custom image.
Since log4j is multi-release jar, there is one thing required:
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 }
See a full working sample here.