Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile a JDK 8 project + a JDK 9 "module-info.java" in Gradle

Tags:

I'm working on a Java library targeting JDK 8, and I'm building it in Gradle 5 using OpenJDK 11. In order to target JDK 8, I'm javac's --release option.

However, I'd also like my library to be JPMS-compatible. In other words:

  • I'd like to provide a module-info.class compiled with --release 9 (option 3 in Stephen Colebourne's scale),
  • while all the rest is compiled with --release 8.

MCVE

build.gradle:

plugins {
    id 'java'
    id 'org.javamodularity.moduleplugin' version '1.4.1' // *
}

repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.6'
}

compileJava.options.compilerArgs.addAll(['--release', '9']) // **

* org.javamodularity.moduleplugin sets --module-path for compileJava

** there's no Gradle DSL for --release yet: #2510

src/main/java/module-info.java:

module pl.tlinkowski.sample {
  requires lombok;
  exports pl.tlinkowski.sample;
}

src/main/java/pl/tlinkowski/sample/Sample.java:

package pl.tlinkowski.sample;

@lombok.Value
public class Sample {
  int sample;
}

This MCVE compiles, but all the classes (instead of only module-info.class) are in JDK 9 class format (v.53).

Other build tools

What I want to do is certainly possible in:

  1. Maven
    • E.g ThreeTen-Extra (their approach boils down to: first compile everything with --release 9, and then compile everything except module-info.java with --release 8).
  2. Ant
    • E.g. Lombok (their approach boils down to: have module-info.java in a separate "source set" - main source set is compiled with --release 8, and "module info" source set is compiled with --release 9).

What I tried

I liked Lombok's approach, so I manipulated the source sets in build.gradle as follows:

sourceSets {
    main { // all but module-info
        java {
            exclude 'module-info.java'
        }
    }
    mainModuleInfo { // module-info only
        java {
            srcDirs = ['src/main/java']
            outputDir = file("$buildDir/classes/java/main")
            include 'module-info.java'
        }
    }
}

Then, I configured a task dependency and added proper --release options to both compilation tasks:

classes.dependsOn mainModuleInfoClasses

compileJava.options.compilerArgs.addAll(['--release', '8'])
compileMainModuleInfoJava.options.compilerArgs.addAll(['--release', '9'])

If I compile now, I get:

error: package lombok does not exist

So I still don't know how to instruct org.javamodularity.moduleplugin to:

  • not use --module-path for main
  • set proper --module-path for mainModuleInfo
like image 765
Tomasz Linkowski Avatar asked Mar 11 '19 11:03

Tomasz Linkowski


1 Answers

EDIT: This functionality is now supported by Gradle Modules Plugin since version 1.5.0.

Here's a working build.gradle snippet:

plugins {
    id 'java'
    id 'org.javamodularity.moduleplugin' version '1.5.0'
}

repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.6'
}

modularity.mixedJavaRelease 8

OK, I managed to get this working by:

  1. disabling org.javamodularity.moduleplugin
  2. removing the custom source set (it wasn't necessary)
  3. adding a custom compileModuleInfoJava task and setting its --module-path to the classpath of the compileJava task (inspired by this Gradle manual)

Here's the full source code of build.gradle:

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.6'
}

compileJava {
    exclude 'module-info.java'

    options.compilerArgs = ['--release', '8']
}

task compileModuleInfoJava(type: JavaCompile) {
    classpath = files() // empty
    source = 'src/main/java/module-info.java'
    destinationDir = compileJava.destinationDir // same dir to see classes compiled by compileJava

    doFirst {
        options.compilerArgs = [
                '--release', '9',
                '--module-path', compileJava.classpath.asPath,
        ]
    }
}

compileModuleInfoJava.dependsOn compileJava
classes.dependsOn compileModuleInfoJava

Notes:

  • it compiles 👍
  • I verified that module-info.class is in JDK 9 format (8th byte is 0x35 → v.53), while other classes are in JDK 8 format (8th byte is 0x34 → v.52) 👍
  • however, disabling org.javamodularity.moduleplugin is unsatisfactory, because it means that tests will no longer run on module path, etc. 👎
like image 99
Tomasz Linkowski Avatar answered Sep 30 '22 08:09

Tomasz Linkowski