2

I'm using Gradle (6.0.1) with the moduleplugin to build an application out of JPMS modules using JDK 13.

However, even with the application plugin applied & its mainClassName set it does not set the ModuleMainClass attribute in module-info.class, so when I jlink it up into a standalone JVM and run java -m mymodule I get this message:

module mymodule does not have a ModuleMainClass attribute, use -m <module>/<main-class>

Digging under the hood it looks like the moduleplugin doesn't change the jar task at all, and the out of the box gradle jar task does not actually use the JDK's jar command, it just zips everything up itself.

As far as I can tell the only way to set the ModuleMainClass attribute in module-info.class is to use the JDK's jar command as so: jar --main-class=CLASSNAME -C <classes dir>.

Is there a way to do this, short of writing my own gradle task? And if not, has anyone got an example of replacing the gradle jar task with one that calls the JDK command?

(Please note this question is not about setting the Main-Class in the jar's MANIFEST.MF - that's easy, but isn't respected when calling java -m <modulename>.)

Robert Elliot
  • 1,372
  • 13
  • 20

1 Answers1

3

It looks like the jar task in gradle does not do a proper job of building the jar to also include the module-main-class in the module-info.class. In fact it doesn't look like it calls the jar command at all which is a bit misleading. So here is an updated version that does this:

jar {
    doLast {
        project.exec {
            workingDir destinationDirectory.get()
            executable 'jar'
            args '--update'
            args '--file', archiveFileName.get()
            args '--main-class', mainClassName
            args '.'
        }
    }
}

Jar command syntax taken in part from this tutorial.

This looks janky, but it works and hopefully some day the Jar task will include that last part.

smac89
  • 39,374
  • 15
  • 132
  • 179
  • 1
    @RobertElliot When you run the jar, there is no need to supply `main-class`. All you need is the `ModuleMainClass` entry in the manifest. If you are going to supply the main class name on the command line, then you need to use the syntax it shows which is `-m moduleName/MainClassName` – smac89 Dec 01 '19 at 22:13
  • I'm not suppling main-class when I run the jar. – Robert Elliot Dec 01 '19 at 22:16
  • @RobertElliot my bad I read your `jar` command as `java`. Let me see if I can scrounge up a test case. I'm pretty sure using the `jar` task like I described above should work, but I may be wrong – smac89 Dec 01 '19 at 22:20
  • For reference: https://issues.apache.org/jira/browse/MJAR-238 – Robert Elliot Dec 01 '19 at 22:31
  • @RobertElliot, I have a project here: https://github.com/smac89/multi-java9-gradle. After you run `gradle run` on check the contents of `src/appMod/build/libs/appMod.jar`, you will see that it has an `ModuleMainClass` in the MANIFEST entry and this was added by doing the same thing I described above. – smac89 Dec 01 '19 at 22:51
  • It does, but it doesn't solve the problem. `./gradlew build && java --upgrade-module-path src/appMod/build/libs:src/greetMod/build/libs -m appMod` results in `module appMod does not have a ModuleMainClass attribute, use -m /` – Robert Elliot Dec 01 '19 at 23:10
  • @RobertElliot I see the problem. Try the new version of the jar task and see if that fixes it – smac89 Dec 01 '19 at 23:38
  • 1
    Yes, that works - also works if I completely delete the `doFirst` that is setting the attribute in the manifest. Would you like to turn that into a separate answer I can accept? – Robert Elliot Dec 02 '19 at 07:25
  • @RobertElliot, I will just edit this to only include the part that answers your question – smac89 Dec 02 '19 at 07:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/203474/discussion-between-robert-elliot-and-smac89). – Robert Elliot Dec 02 '19 at 16:07