Migrate to Jigsaw Modules using Java 11

Hi,
I would like to transition a java 11/gradle 7.1.1 project to use jigsaw modules.

To achive this the code has been prepared by moving the classes into packages that will be the jigsaw modules. Now the goal is to to have the first two modules. Basically this means that one module is extracted and the rest of the code is changed into a module as well. From there it should be easy to extract all the other modules.

While all of this was quite easy to setup in a tiny demo project it proves to be tricky in the details. It was possible to set up the modules and get them to compile and run however it is difficult to get it working in the actual project.

Now I am not so sure what is even configured correct and what is not. I am not even sure gradle supports everything we need (regarding to jigsaw) out of the box or whether plugins are a must for this. One plugin that sounds promising is: GitHub - java9-modularity/gradle-modules-plugin: This Gradle plugin helps working with the Java Platform Module System However if it is somehow possible to achive a running config without using a plugin that would be nice since it makes it harder to be able to upgrade gradle from time to time.

The build.gradle in root contains a lot of helper tasks and is holds configuration for the subprojects:

allprojects {
    group 'my.group'

    repositories {
        maven {
            url 'http://server:8081/repository/'
            allowInsecureProtocol = true
        }
    }
}

subprojects {
    apply plugin: 'de.jjohannes.extra-java-module-info'

    afterEvaluate {
        java {
            modularity.inferModulePath = true

            withSourcesJar()
            withJavadocJar()

            toolchain {
                languageVersion = JavaLanguageVersion.of(11)
                vendor = JvmVendorSpec.ADOPTOPENJDK
            }
        }

        compileJava {
            modularity.inferModulePath = true
        }

        compileTestJava {
            modularity.inferModulePath = true
        }

        test {
            modularity.inferModulePath = true

            useJUnitPlatform()

            testLogging {
                outputs.upToDateWhen { false }
                showStandardStreams = true
            }
            minHeapSize = "512m"
            maxHeapSize = "3g"
            
            // is necessary 
            forkEvery = 1
            maxParallelForks = 3

            reports {
                junitXml.required = true
                junitXml.outputPerTestCase = true
                html.required = false
            }

            // tests that create a file need a dir to store it - it should be deleted with the clean target
            def testTempResDir = Paths.get(buildDir.toString(), "tmp", "test", "res").toString()
            mkdir testTempResDir
            systemProperty "test.temp.res.dir", testTempResDir
            systemProperty "test.root.dir", rootDir
        }

        javadoc {
            modularity.inferModulePath = true

            excludes = ['**/*.form', '**/*.xml', '**/*.html',
                        '**/*.ttf', '**/*.png', '**/*.txt',
                        '**/*.jrxml', '**/*.jasper', '**/*.jpg',
                        '**/*.csv', '**/*.ftl', '**/*.gpg', '**/*.wsdl',
                        '**/META-INF/', '**/*.bak']
        }
    }
}

Can someone point out a bit how the module path is handled in gradle and also how it should be handled regarding jar manifests and run.bat scripts. I have noticed that the startup scripts that are generated have paths so long they just wouldn’t start (assuming lots of dependencies). Can I add the jars to the manifest like we have added them to the classpath? This is how we did that before:

project.configurations.implementation.setCanBeResolved(true)
project.configurations.runtimeOnly.setCanBeResolved(true)

jar {
    manifest {
        attributes(
                'Main-Class': application.mainClass.get(),
                'Class-Path': configurations.implementation.collect { "lib/$it.name" }.join(' ') + " " +
                        configurations.runtimeOnly.collect { "lib/$it.name" }.join(' ')
}

From most of my searches modules are still something that aren’t adopted that well so many dependencies aren’t compatible still. Is it better to wrap them with an automatic module plugin or to read everything from the unnamed module? If so is it applied to the project in a good way with the compilerArgs there or can that be done better?
Does gradle facilitate the testing with modules in any way? I couldn’t find anything useful beside the aformentioned plugin that helps with both whitebox and blackbox testing.

With this setup the project can be compiled and the tests run - however the application just won’t run and fails with an exception mentioning that the module cannot be found:

> Task :suite:run FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module suite not found

The module-info.java from suite starts with open module suite {...} so the name should be fine.
Also if I create another module and place a main in there the application would start just fine (well at least it would run the main of that module)

Intellij also wouldn’t show any signs of the module-info being wrong or anything else in the project setup that is obviously wrong.

The project is structured like that:

root
├── modules
│   ├── core
│   │   ├── src
│   │   │   ├── main
│   │   │   │   ├── java
│   │   │   │   │   ├── my.packages
│   │   │   │   │   ├── module-info.java
│   │   │   │   ├── test
│   │   └── build.gradle 
│   ├── suite (rest)
│   │   ├── src
│   │   │   ├── main
│   │   │   │   ├── java
│   │   │   │   │   ├── my.other.packages
│   │   │   │   │   ├── module-info.java
│   │   │   │   ├── test
│   │   └── build.gradle 
├── build.gradle       
└── settings.gradle

settings.gradle:

rootProject.name = 'myApp'
include ':suite', ':core'
project(':suite').projectDir = new File('modules/suite')
project(':core').projectDir = new File('modules/core')

suite:build.gradle

plugins {
    id 'application'
    id 'de.jjohannes.extra-java-module-info'
}

extraJavaModuleInfo {
    failOnMissingModuleInfo = false
    automaticModule("jasperreports-6.16.0.jar", "net.sf.jasperreports")
}

dependencies {
    implementation project(':core')
    implementation 'net.sf.jasperreports:jasperreports:6.16.0'
}

compileJava {
    modularity.inferModulePath = true

    doFirst {
        options.compilerArgs = [
                '--add-reads', 'suite=ALL-UNNAMED'
        ]
    }
}

application {
    mainModule = 'suite'
    mainClass = 'my.other.packages.Main'
}

core:build.gradle

plugins {
    id 'java-library'
    id 'de.jjohannes.extra-java-module-info'
}

extraJavaModuleInfo {
    failOnMissingModuleInfo = false
    automaticModule("commons-cli-1.4.jar", "org.apache.commons.cli")
}

dependencies {
    implementation 'commons-cli:commons-cli:1.4'
}

Any help would be much appreciated.
Thanks for reading.