Feature request: Separate compilation of module-info.java
See original GitHub issueOverview
I’d like to request a feature for library developers who need to target JDK 6-8 but who also want to provide a module-info.class
(as per Option 3 by @jodastephen).
The proposed feature has two parts:
- Low-level API: separate compilation of
module-info.java
. - High-level API: easy setting of javac
--release
option, separately for main Java code and formodule-info.java
.
Justification
General
Library maintainers need to target JDK 6-8 because these JDKs (especially JDK 8) are still very popular (taking ~95% share in 2018, and predicted to take ~90% in 2019).
Still, those maintainers could make the most of JPMS by:
- providing
module-info.class
for consumers who put the library on module path, - compiling
module-info.java
against the remaining classes of this module and against other modules (which provides better encapsulation and prevents split packages).
So providing a module-info.class
is like saying:
Hey, I’m JPMS-compatible! You can safely put me on the module path — I guarantee to have no split packages!
To sum up, I see this feature request as great means of promoting JPMS! Would you agree, @paulbakker, @sandermak?
Examples
Here are some popular libraries that provide module-info.class
while targeting JDK 6-8, e.g.:
- Maven:
- ThreeTen-extra (JDK 9
module-info.class
in root dir) - Google Gson - not released yet (JDK 9
module-info.class
in root dir) - SLF4J - not released yet (JDK 9
module-info.class
inMETA-INF/versions/9
- Multi-Release JAR)
- ThreeTen-extra (JDK 9
- Ant:
- Lombok (JDK 9
module-info.class
in root dir)
- Lombok (JDK 9
There are also some libraries that:
- consider doing that: https://github.com/google/guava/issues/2970 (in Maven)
- wanted (want?) to do that: https://github.com/vavr-io/vavr/issues/2230 (in Gradle)
- or even have a separate modular artifact (although I’m not sure why that’s necessary): log4j-api-java9 (in Maven)
Vavr /ˈveɪ.vɚ/
Let’s have a closer look at vavr’s case:
- It’s a quite popular library (3000+ Github stars) targeting JDK 8.
- Its creator, @danieldietrich, is (or was?) a believer in JPMS.
- However, as https://github.com/vavr-io/vavr/issues/2230#issuecomment-374792372 shows, he backed out of it (partly because the required Gradle configuration was too complex).
- I personally think it’s a shame if such a popular library cannot be easily shipped with a
module-info.class
while still targeting JDK 8.
Proposed DSL
Note: Everything in this section (especially naming) is TBD.
Low-level API
Extra property on moduleOptions
extension:
build.gradle
compileJava.moduleOptions.compileModuleInfoSeparately = true
build.gradle.kts
tasks {
compileJava.moduleOptions.compileModuleInfoSeparately = true
}
High-level API
Special modularity
extension with two functions:
- for releasing to JDK 6-8:
mixedJavaRelease(int mainJavaRelease, int moduleInfoJavaRelease = 9)
- for releasing to JDK 9+
standardJavaRelease(int mainJavaRelease)
For example, the most common configuration (JDK 8 + JDK 9 module-info.class
) would be a one-liner:
build.gradle
modularity.mixedJavaRelease 8
build.gradle.kts
modularity.mixedJavaRelease(8)
Proposed behavior
module-info.java
location
I propose to leave module-info.java
in src/main/java
(I don’t see any need to put it in a separate source set directory).
module-info.class
location
There are two places where module-info.class
can end up:
- in the root output directory (natural; corresponds to
module-info.java
location) - in
META-INF/versions/9
(Multi-Release JAR, AKA MRJAR)
Having read a post on MRJARs by @melix, I’m rather suspicious of MRJARs, and so I prefer option 1.
On the other hand, @gunnarmorling claims that it’s better to use option 2 because module-info.class
“will be out of the way in most cases when being used on an older Java version”. But I don’t really know why, because as far as I understand, any tool that scans the classpath (like Guava’s ClassPath
) will return module-info.class
no matter where it is (based on its .class
extension). @gunnarmorling, would you care to give me a pointer here?
Alternatives
ModiTect
It’s fair to mention ModiTect (by @gunnarmorling) and its Gradle plugin (by @siordache) here.
However, ModiTect does much more than I request here: ModiTect actually generates module-info.class
from a special notation (there’s even no edit: it can actually parse module-info.java
theremodule-info.java
). Personally, I find it too complex for my needs.
Badass-Jar plugin
@siordache also created a Gradle plugin that lets one “seamlessly create modular jars that target a Java release before 9”.
It looks quite nice, however:
I’m not sure how it plays along withorg.javamodularity.moduleplugin
(and I’d like to use it for patching modules, testing on module-path, etc.)- in order to build the proper JAR and make sure your
module-info.java
is correct, you actually have to run the build twice (./gradlew jar
and./gradlew -PjavaCompatibility=9 jar
) - too complex - it doesn’t use javac’s
--release
option, which (as opposed to using-source
and-target
) guarantees that only the right APIs are referenced (e.g. it throws when compilingoptional.isEmpty()
with--release 8
) it produces only Multi-Release JARs, and I’m somewhat skeptical about them (as I mentioned before)
Related StackOverflow questions
- Compile a JDK 8 project + a JDK 9 “module-info.java” in Gradle (mine)
- Gradle: Building a modularized library that is compatible with Java 8
- Compile Java codebase into 10 and 8 with Gradle
- Java 9 --release flag doesn’t appear to be working in Gradle
- Gradle Multi Project with Java 9 and Java 8
- Is it possible to mix Java 8 and Java 9 source code in the same project without using compiler flags?
Issue Analytics
- State:
- Created 5 years ago
- Comments:14
Top GitHub Comments
Merged the work done by @tlinkowski! Will be in the next release (which I plan to publish today)
@tlinkowski This is great! Thanks for the detailed writeup and PR. I’m fully onboard with supporting this, seems like a great feature to further increase JPMS adoption. I’ll need a few days before I can give it the attention it deserves (trip coming up this weekend), but I’ll get to it as soon as possible.