Fork me on GitHub

Java 9+ projects with module-info

If a project with a module-info.java file does not need to be compatible with Java 8 or earlier environment, there is nothing special to do.

Java 8 projects with module-info

Using the maven-compiler-plugin

Projects that want to be compatible with older versions of Java (i.e, 8 or below bytecode and API), but also want to provide a module-info.java for use on Java 9+ runtime, must be aware that they need to call javac twice:

  1. the module-info.java must be compiled with release set to 9 or later,
  2. the rest of the sources must be compiled with the lower expected compatibility version of source/target.

A way to do this is by having two execution blocks, as described below:

  1. default default-compile execution with release set to 9 or later,
  2. additional custom base-compile execution with expected target compatibility.

The following snippet gives an example. This snippet assumes that the JDK used by Maven supports the --release 8 option. It may not be the case for all JDKs, as newer JDKs may drop the support of Java versions that are too old.

<project>
  [...]
  <build>
    [...]
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>...</version>
        <executions>
          <execution>
            <id>default-compile</id>
            <configuration>
              <release>9</release>
              <!-- no excludes: compile everything to ensure module-info contains right entries -->
            </configuration>
          </execution>
          <execution>
            <id>base-compile</id>
            <goals>
              <goal>compile</goal>
            </goals>
            <configuration>
              <release>8</release>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
    [...]
  </build>
  [...]
</project>

If the JDK used by Maven does not support the --release 8 option, then projects which want to be compatible with old Java versions need to use two different JDKs for the two executions. Using a toolchains configuration, it is possible to achieve this, even if more complex. In above snippet, the following fragment:

              <release>8</release>

can be replaced by:

              <source>1.8</source>
              <target>1.8</target>
              <jdkToolchain>
                <version>1.8</version>
              </jdkToolchain>

Using the moditect-maven-plugin

A clean way to generate the module-info.class file without compiling the sources twice is to use the moditect-maven-plugin.

The goal that adds a module descriptor to the project jar is add-module-info.

With this plugin you can keep the maven.compiler.release property set to 8, i.e. the same Java version for the whole build.

A minimal example looks like this:

<project>
  <properties>
    <maven.compiler.release>8</maven.compiler.release>
    <project.package.name>com.example.project</project.package.name>
  </properties>

  <build>
    <plugin>
      <groupId>org.moditect</groupId>
      <artifactId>moditect-maven-plugin</artifactId>
      <version>1.3.0.Final</version>
      <executions>
        <execution>
          <id>add-module-infos</id>
          <phase>package</phase>
          <goals>
            <goal>add-module-info</goal>
          </goals>
          <configuration>
            <module>
              <moduleInfo>
                <name>${project.package.name}</name>
                <exports>
                  ${project.package.name};
                </exports>
              </moduleInfo>
            </module>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </build>
</project>

Summary

  • maven-compiler-plugin + two compiler runs – flexible but a bit cumbersome.
  • moditect-maven-plugin – creates module‑info.class in a single build pass, keeps the target Java version (e.g., 8) unchanged, and reduces build‑time complexity.