Java modules (JPMS) : Compile all modules at once

I started to get interested in gradle while working with java modules (JPMS). To test several things I used the example provided by gradle (Kotlin version).

https://docs.gradle.org/current/samples/sample_java_modules_multi_project.html

On this project we have several modules and during the gradle build, each java module is compiled independently by gradle to create the final application. (Gradle compiles first “list” then “utilities” then “application”).

One question I have is if it would be possible for gradle to compile all modules at once (one task). As it is explained here: Project Jigsaw: Quick Start Guide (Multi-module compilation part) it is possible to compile several java modules with one javac compilation command. Can gradle do something similar?

Hello @KontainPluton

i am new to gradle also

i think so but i did not tried the following yet

if you used the java plugin there compile task you can configure it

here the full details of this task JavaCompile - Gradle DSL Version 7.5

it has property called options

that has a property called compilerArgs which is a list of string you add the compiler argument there

not sure if there much easier way for that or not

hope that help and have a nice day :slight_smile:

1 Like

I’d say, the question is why you should want to do that?
It if for sure somehow possible, but you most probably should not do it for various reasons.
So the question is, what you would try to achieve with that, then someone can maybe advice on how to properly solve your use-case.

Hello thank you for your answers, there are of course several reasons why I am asking this.

To be honest, I’m working on the possibility of switching a large project from maven to gradle.
And one of the questions for this switch was if we could do multi-module java compilation in gradle; because this feature would bring several interesting things:

  • For the javadoc, maven creates documentation for each module. If you want a complete javadoc you have to use maven aggregate. This complete javadoc allows to have links between modules and other information. It would be interesting to have something similar in gradle when creating the documentation. (without going through a third party command like maven aggregate)

  • This multi-module compilation mechanism also allows to activate some features of the java compiler, like the one to raise errors if there are invalid links between the different modules.

  • Other java tools could also be used on the whole project, instead of being used on each module independently.

Overall the goal is to be able to “aggregate” the modules in order to have a much more complete use of some tools. And to do this, we need to be able to do this multi-module compilation.


And going back to the previous post, I was able to test a few things using compilerArgs.

tasks.compileJava {
    options.compilerArgs.add("-d")
    options.compilerArgs.add("mods")
    options.compilerArgs.add("--module-source-path")
    options.compilerArgs.add("\"../*/src/main/java/\"")

I tried to change the sourceSets to add the other modules, and even to add manually the list of sources in the arguments. But in all cases I get several errors:

/application/src/main/java/org/gradle/sample/application/MessageUtils.java:1: error: not in a module on the module source path
package org.gradle.sample.application;

/application/src/main/java/module-info.java:1: error: module not found on module source path
module application {

Same errors on other .java files of other modules

There is at least nothing built-in as far as I’m aware.
You can probably somehow make it with some amount of configuration, but you probably really shouldn’t do it.

I’m not sure what you mean by warnings about invalid links, but “other java tools” is imho even worse if you do it on all, because if you do it on each module, only the changed module needs to be rechecked / reprocessed / recompiled, …

Regarding JavaDoc, there are indeed community plugins to aggregate JavaDoc from multiple projects into one big doc and you can also wire this in, so that it is automatically done after building javadoc so that you don’t have to manually trigger it.
But you can also simply configure the JavaDoc so that links between the independent module docs work properly. If you for example look at Javacord/build.gradle.kts at master · Javacord/Javacord · GitHub you see how javacord-core JavaDoc task is configured to automatically link to javacord-api docs.

I 'm coming back to this post after several moths to give the solution to the problem.
For testing purposes (to see the possibilities of gradle) I wanted to be able to compile in one time all the java modules in a gradle project. To do this, via the command lines, we can use javac with the option --module-source-path where we specify all the modules to compile.

Example : javac -d mods --module-source-path src $(find src -name "*.java")
More information here : Project Jigsaw: Quick Start Guide

So I tried to do this in a gradle project, and here is the result : GitHub - KontainPluton/gradle-modules-working

This project contains a build.gradle.kts at the root, and 5 “main” modules in src/main/java/. (There are also two test modules in src/test/java.

And here is the content of build.gradle.kts, with the compileJava and compileTestJava tasks overridden. :

tasks.compileJava {
    options.compilerArgs.add("--module-source-path")
    options.compilerArgs.add(files("src/main/java").asPath)
    options.compilerArgs.add("--module-path=${classpath.asPath}")
}

tasks.compileTestJava {
    options.compilerArgs.add("--module-source-path")
    options.compilerArgs.add(files("src/test/java").asPath)
    options.compilerArgs.add("--patch-module")
    options.compilerArgs.add("another=${tasks.compileJava.get().destinationDirectory.asFile.get().path}/another")
    options.compilerArgs.add("--patch-module")
    options.compilerArgs.add("example=${tasks.compileJava.get().destinationDirectory.asFile.get().path}/example")
    options.compilerArgs.add("--module-path=${classpath.asPath}")
}

tasks.test {
    useJUnit()
    testLogging {
        events("PASSED", "FAILED", "SKIPPED", "STANDARD_OUT")
    }
    maxHeapSize = "1G"

    val args = listOf(
        "--patch-module","another=${tasks.compileJava.get().destinationDirectory.asFile.get().path}/another",
        "--patch-module","example=${tasks.compileJava.get().destinationDirectory.asFile.get().path}/example",
        "--add-modules","another",
        "--add-modules","example",
        "--module-path=${classpath.asPath}"
    )
    jvmArgs(args)
}

(Full file on github)


All modules are compiled at once by the java compiler. This may not be a useful and viable solution on projects. But if one day someone asks this question, here is a solution.

Note : A useful link to understand testing in the world of java modules :slight_smile:
Testing In The Modular World | sormuras.github.io

1 Like