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

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 = 8

  • configure 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 include 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.

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'

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)
}