'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. I did it in the following way:
- provide a module-info.java
- added the (experimental) jigsaw plugin in
build.gradle
- Manually made changes according to the guide on the gradle site instead of using the jigsaw plugin.
So far, both approaches work fine, and I can use the generated Jar in Java 9 projects.
The problem is, the resulting Jar is not compatible with Java 8 although I used no Java 9 features except the module-info.java
. When I set targetCompatibility = 8
, an error message tells me to also set sourceCompatibility = 8
accordingly. Which then rejects the module-info.java
for which I should set sourceCompatibility = 9
.
How can this be solved?
I removed the jigsaw plugin again, and tried this, but am stuck:
- set
sourceCompatibility = 8
andtargetCompatibility = 8
- create a new source set
moduleInfo
that contains the single filemodule-info.java
- set
sourceCompatibility = 9
andtargetCompatibility = 9
for the new sourceset
Now compilation works, and Gradle uses Java 9 when it tries to compile the module-info.java
. However, modules (in this case log4j) are missing, and I get this error:
: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):
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 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;
}
Solution 1:[1]
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:
sourceCompatibility = 8
targetCompatibility = 8configure the module name
ext.moduleName
=com.dua3.utility
add a new sourceset consisting only of
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:
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 includemoduleInfo
: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.
I finally ended up with this version of 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'
Solution 2:[2]
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.
With this plugin, you don't have to create a custom source set, and you only need to call modularity.mixedJavaRelease
method.
Here's a sample of how to apply the plugin to one's main 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)
}
Solution 3:[3]
I have developed a Gradle plugin for this: 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
.
It is not implemented by calling 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.
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. For a Java 8 project containing module-info.java
, you only need to do this:
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).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Prags |
Solution 2 | Tomasz Linkowski |
Solution 3 | Glavo |