简体   繁体   English

Gradle:构建兼容Java的模块化库8

[英]Gradle: Building a modularized library that is compatible with Java 8

So Java 9 is there, soon to be followed by Java 10. Time we should make our libraries ready for use in Java 9 projects.所以 Java 9 就在那里,很快就会有 Java 10。是时候让我们的库准备好在 Java 9 项目中使用了。 I did it in the following way:我是通过以下方式做到的:

  1. provide a module-info.java提供模块信息.java
  2. added the (experimental) jigsaw plugin in build.gradlebuild.gradle中添加了(实验性的) 拼图插件
  3. Manually made changes according to the guide on the gradle site instead of using the jigsaw plugin.根据 gradle 站点上的指南手动进行更改,而不是使用拼图插件。

So far, both approaches work fine, and I can use the generated Jar in Java 9 projects.到目前为止,这两种方法都可以正常工作,我可以在 Java 9 个项目中使用生成的 Jar。

The problem is, the resulting Jar is not compatible with Java 8 although I used no Java 9 features except the module-info.java .问题是,生成的 Jar 与 Java 8 不兼容,尽管我没有使用 Java 9 功能,除了module-info.java When I set targetCompatibility = 8 , an error message tells me to also set sourceCompatibility = 8 accordingly.当我设置targetCompatibility = 8时,一条错误消息告诉我还相应地设置sourceCompatibility = 8 Which then rejects the module-info.java for which I should set sourceCompatibility = 9 .然后拒绝我应该为其设置sourceCompatibility = 9module-info.java

How can this be solved?如何解决?

I removed the jigsaw plugin again, and tried this, but am stuck:我再次删除了拼图插件,并尝试了这个,但卡住了:

  1. set sourceCompatibility = 8 and targetCompatibility = 8设置sourceCompatibility = 8targetCompatibility = 8
  2. create a new source set moduleInfo that contains the single file module-info.java创建一个新的源集moduleInfo ,其中包含单个文件module-info.java
  3. set sourceCompatibility = 9 and targetCompatibility = 9 for the new sourceset为新的源集设置sourceCompatibility = 9targetCompatibility = 9

Now compilation works, and Gradle uses Java 9 when it tries to compile the module-info.java .现在编译工作正常,并且 Gradle 在尝试编译module-info.java时使用 Java 9 。 However, modules (in this case log4j) are missing, and I get this error:但是,缺少模块(在本例中为 log4j),我收到此错误:

:compileJava UP-TO-DATE
:processResources NO-SOURCE
:classes UP-TO-DATE
:jar UP-TO-DATE
:sourcesJar UP-TO-DATE
:assemble UP-TO-DATE
:spotbugsMain UP-TO-DATE
:compileModuleInfoJava
classpath:
compilerArgs: [--module-path, , --add-modules, ALL-SYSTEM]
D:\git\utility\src\module-info\java\module-info.java:14: error: module not found: org.apache.logging.log4j
    requires org.apache.logging.log4j;
                               ^
warning: using incubating module(s): jdk.incubator.httpclient
1 error
1 warning
:compileModuleInfoJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileModuleInfoJava'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
5 actionable tasks: 1 executed, 4 up-to-date

This is the build.gradle used (Gradle version is 4.5.1):这是使用的build.gradle (Gradle 版本是 4.5.1):

plugins {
  id "com.github.spotbugs" version "1.6.0"
}

apply plugin: 'maven'
apply plugin: 'maven-publish'
apply plugin: 'java-library'
apply plugin: 'com.github.spotbugs'

sourceCompatibility = 8
targetCompatibility = 8

group = 'com.dua3.utility'

repositories {
    mavenLocal()
    jcenter()
}

dependencies {
  compile     group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
  testRuntime group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'

  // Use JUnit test framework
  testImplementation 'junit:junit:4.12'
}

ext.moduleName = 'com.dua3.utility' 

sourceSets {
    moduleInfo {
        java {
            srcDir 'src/module-info/java'            
        }
    }
}

compileModuleInfoJava {
    sourceCompatibility = 9
    targetCompatibility = 9

    inputs.property("moduleName", moduleName)

    doFirst {
        options.compilerArgs = [
            '--module-path', classpath.asPath,
            '--add-modules', 'ALL-SYSTEM'
        ]
        classpath = files()  
        System.out.println("classpath: "+classpath.asPath)
        System.out.println("compilerArgs: "+options.compilerArgs)
    }
}

tasks.withType(com.github.spotbugs.SpotBugsTask) {
    reports {
        xml.enabled false
        html.enabled true
    }
}

task sourcesJar(type: Jar, dependsOn: classes) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

task javadocJar(type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

artifacts {
    archives sourcesJar
// fails with jigsaw:    archives javadocJar
}

defaultTasks 'build', 'publishToMavenLocal', 'install'

And this is module-info.java :这是module-info.java

module com.dua3.utility {
    exports com.dua3.utility;
    exports com.dua3.utility.io;
    exports com.dua3.utility.jfx;
    exports com.dua3.utility.swing;
    exports com.dua3.utility.lang;
    exports com.dua3.utility.math;
    exports com.dua3.utility.text;

    requires javafx.controls;
    requires javafx.web;
    requires java.xml;
    requires java.desktop;
    requires org.apache.logging.log4j;
}

OK, I finally got it working . 好的,我终于搞定了 In case anyone else wants to know how to do it, this is what I have done: 如果其他人想知道如何做,这就是我所做的:

  • set the Java version to 8, so that the library will be usable by Java 8 applications: 将Java版本设置为8,以便Java 8应用程序可以使用该库:

    sourceCompatibility = 8 sourceCompatibility = 8
    targetCompatibility = 8 targetCompatibility = 8

  • configure the module name 配置模块名称

    ext.moduleName = com.dua3.utility ext.moduleName = com.dua3.utility

  • add a new sourceset consisting only of module-info.java : 添加仅包含module-info.java的新module-info.java

      sourceSets { moduleInfo { java { srcDir 'src/module-info/java' } } } 
  • set compatibility to Java 9 for the moduleInfo, sourceSet, configure modules, and set the output directory: 为moduleInfo,sourceSet,configure modules设置与Java 9的兼容性,并设置输出目录:

      compileModuleInfoJava { sourceCompatibility = 9 targetCompatibility = 9 inputs.property("moduleName", moduleName) doFirst { classpath += sourceSets.main.compileClasspath options.compilerArgs = [ '--module-path', classpath.asPath, '--add-modules', 'ALL-SYSTEM,org.apache.logging.log4j', '-d', sourceSets.main.output.classesDirs.asPath ] } } 
  • configure the jar task to include moduleInfo : 配置jar任务以包含moduleInfo

     jar { from sourceSets.main.output from sourceSets.moduleInfo.output } 

In case you are using the SpotBugs plugin, you also have to configure the sourceSet explicitly because it will otherwise fail when it tries to process the ModuleInfo sourceSet. 如果您使用的是SpotBugs插件,则还必须显式配置sourceSet,否则在尝试处理ModuleInfo sourceSet时会失败。

I finally ended up with this version of build.gradle : 我终于得到了这个版本的build.gradle

plugins {
  id "com.github.spotbugs" version "1.6.0"
}

apply plugin: 'maven'
apply plugin: 'maven-publish'
apply plugin: 'java-library'
apply plugin: 'com.github.spotbugs'

sourceCompatibility = 8
targetCompatibility = 8

group = 'com.dua3.utility'

repositories {
    mavenLocal()
    jcenter()
}

dependencies {
  compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
  testRuntime group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'

  // Use JUnit test framework
  testImplementation 'junit:junit:4.12'
}

ext.moduleName = 'com.dua3.utility' 

sourceSets {
    moduleInfo {
        java {
            srcDir 'src/module-info/java'            
        }
    }
}

compileModuleInfoJava {
    sourceCompatibility = 9
    targetCompatibility = 9

    inputs.property("moduleName", moduleName)

    doFirst {
        classpath += sourceSets.main.compileClasspath

        options.compilerArgs = [
            '--module-path', classpath.asPath,
            '--add-modules', 'ALL-SYSTEM',
            '-d', sourceSets.main.output.classesDirs.asPath
        ]
    }
}

jar 
{
    from sourceSets.main.output
    from sourceSets.moduleInfo.output
}

spotbugs {
    sourceSets = [sourceSets.main]
}

tasks.withType(com.github.spotbugs.SpotBugsTask) {
    reports {
        xml.enabled false
        html.enabled true
    }
}

task sourcesJar(type: Jar, dependsOn: classes) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

task javadocJar(type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

artifacts {
    archives sourcesJar
    archives javadocJar
}

defaultTasks 'build', 'publishToMavenLocal', 'install'

The question is over a year old, but in case anyone stumbles here, this functionality is now supported by Gradle Modules Plugin since version 1.5.0 . 问题已经超过一年了,但是如果有人在这里遇到麻烦,那么从1.5.0版本开始, Gradle Modules Plugin现在支持此功能。

With this plugin, you don't have to create a custom source set, and you only need to call modularity.mixedJavaRelease method. 使用此插件,您不必创建自定义源集,只需调用modularity.mixedJavaRelease方法即可。

Here's a sample of how to apply the plugin to one's main build.gradle : 这是一个如何将插件应用于一个主build.gradle

plugins {
  // your remaining plugins here

  id 'org.javamodularity.moduleplugin' version '1.5.0' apply false
}

subprojects {
  // your remaining subproject configuration here

  apply plugin: 'org.javamodularity.moduleplugin'
  modularity.mixedJavaRelease 8 // sets "--release 8" for main code, and "--release 9" for "module-info.java"

  // test.moduleOptions.runOnClasspath = true // optional (if you want your tests to still run on classpath)
}

I have developed a Gradle plugin for this: https://github.com/Glavo/module-info-compiler我为此开发了一个 Gradle 插件: https://github.com/Glavo/module-info-compiler

I have tried Gradle Modules Plugin, but there are still some troublesome problems, so I developed this plugin, a compiler specifically used to compile module-info.java .试过Gradle Modules Plugin,但是还是有些麻烦的问题,所以开发了这个插件,专门用来编译module-info.java的编译器。

It is not implemented by calling javac.它不是通过调用javac实现的。 It is a complete compiler that can run above Java 8. It recognizes the syntax of module-info.java and generates the corresponding module-info.class file according to it.是一个可以运行在Java上面的完整编译器 8.识别module-info.java的语法,并根据它生成对应的module-info.class文件。

It only checks the grammar, and does not actually check those packages, classes or modules, so it can work without configuration of any module path.它只检查语法,并不实际检查那些包、类或模块,因此它可以在不配置任何模块路径的情况下工作。

This Gradle plugin has processed everything for you.这个 Gradle 插件已经为你处理好了一切。 For a Java 8 project containing module-info.java , you only need to do this:对于包含module-info.java的 Java 8 项目,你只需要这样做:

plugins {
    id("java")
    id("org.glavo.compile-module-info-plugin") version "2.0"
}

tasks.compileJava {
    options.release.set(8)
}

This answer copy the answer written by myself under another question ( https://stackoverflow.com/a/72074642/7659948 ).这个答案复制了自己在另一个问题下写的答案( https://stackoverflow.com/a/72074642/7659948 )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM