使用Gradle向运行时映像添加依赖项

时间:2018-12-13 13:01:53

标签: java gradle javafx log4j

我不知道如何添加依赖项。我的模块需要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,要求   应用

我检查了build中的libs目录,但没有log4j jar。如何告诉gradle为jlink任务添加依赖项?

1 个答案:

答案 0 :(得分:4)

问题

这是您在jlink任务中所拥有的:

'--module-path', "libs${File.pathSeparatorChar}${fx_jmods}"

这意味着您要添加以下项的依赖关系:

  • libs:基本上是模块helloFX的jar。这是jar任务的结果,该任务仅包括项目的类和资源,但不包括其依赖项。

  • fx_jmods:这是JavaFX jmods的路径。

但是当您运行时,会出现此错误:

  

错误:应用程序需要模块org.apache.logging.log4j找不到

该错误表示module-path命令的jlink未完成,并且无法解析所有必需的依赖项。如上所述,我们仅包含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是您的运行平台。

请注意,libs文件夹现在包含 all 模块路径所需的依赖项,并且JavaFX-*-$ platform jars 包含本机库,因此,在module-path选项中不再需要jmods。这样就足够了:

'--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

  

当前基于给定的自动模块,您不能在要使用jlink生成运行时映像的项目中使用log4j-core。

我已将其添加到我的主班:

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任务中失败

 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,可以成功使用它。

总之,这是必需的:

成绩文件:

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;

主类:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(MainApp.class);

...
logger.info("hellofx!");

logging.properties来自here,而logback.xml来自here,与您的主要班级相符。

正在运行./gradlew./gradlew jlink,并且build/image/bin/HelloFX都可以运行,并且消息已记录到控制台。

编辑(2)

reporting向badass-jlink-plugin的问题跟踪器发出问题之后,此问题已解决,并且从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