Search code examples
javamavenjava-8java-9java-platform-module-system

How to use maven with both jigsaw and jre 8 support?


I'm building a java project with maven. I want to make sure several things:

  1. the built jar CAN run on jre8.
  2. the built jar CAN run on jre9, with module/jigsaw.
  3. the built jar CAN be put on maven central.

How should I configure the maven-compiler-plugin? thanks.

the original repo is at https://github.com/cyanpotion/SDL_GameControllerDB_Util

right now I can pass 2 and 3, but the output jar seems cannot run on jre8.


Solution

  • A multi-release jar can be used to accomplish this purpose; however, if you are only after Jigsaw module support, a dual compilation configuration of Java 8 and 9 with the maven-compiler-plugin is sufficient (demonstrated below).

    The following is a build configuration that is able to maintain JRE 8 support, but is compatible with being used as a module in JRE 9+. This can be tweaked to support a project as far back as JRE 1.6, if necessary.

    Overview of the following build configuration:

    • Enforce a $JAVA_HOME JDK 9+ so that module-info.java can be compiled and validated against the sources (ensures that all module dependencies are correctly referenced).
    • Changes the default-compile execution to NO-OP. By specifying <release>8</release> (or target/source flags), some IDEs during Maven auto-importation (tested with IntelliJ) will assume the language level for this project is Java 8, and produce errors about the project's module-info.java.
    • Compiles all sources (including module-info.java) for Java 9; this ensures that the module-info.java includes any dependencies used without the project sources.
    • Compile all sources (excluding module-info.java) for Java 8, overwriting all previously compiled Java 9 classes with Java 8 classes; this means the only Java 9 class (class level 53+) will be module-info.class. All other classes should now be executable on a compliant JRE 8.

    Notes:

    • A caveat is this will compile sources twice, once for Java 8 and another for Java 9. This may significantly increase build times for larger projects (but negligible for smaller projects). This may resolved by placing module-info.java in another source directory (e.g. src/main/java9) and configuring a multi-release JAR (see the first link at the beginning of this message). Note that for proper IDE auto-importation and marking this additional Java 9 source directory correctly, a NO-OP execution with org.codehaus.mojo:build-helper-maven-plugin can be used.
    • Only module-info.java will have Java 9 support, all other classes are limited to classes/methods/fields/code available in JDK/JRE 8.
    • Some tooling may need to be updated in your project configuration if it does not support Java 9 module-info.class. Some older variants of the default Maven plugins do not have sufficient support for modules.
    <build>
        <plugins>
            <!-- ensure the project is compiling with JDK 9+ -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>3.0.0-M3</version>
                <executions>
                    <execution>
                        <id>enforce-jdk9</id>
                        <goals>
                            <goal>enforce</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <requireJavaVersion>
                                    <version>[1.9,)</version>
                                    <message>JDK 9+ is required for compilation</message>
                                </requireJavaVersion>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- compile sources -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <executions>
                    <!-- disable default phase due to fixed id and position in lifecycle -->
                    <execution>
                        <id>default-compile</id>
                        <phase>none</phase>
                        <!-- specify source/target for IDE integration -->
                        <configuration>
                            <release>9</release>
                        </configuration>
                    </execution>
                    <!-- compile sources with Java 9 to generate and validate module-info.java -->
                    <execution>
                        <id>java-9-module-compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <release>9</release>
                        </configuration>
                    </execution>
                    <!-- recompile sources as Java 8 to overwrite Java 9 class files, except module-info.java -->
                    <execution>
                        <id>java-8-compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <!-- specify JDK 9+ release flag to ensure no classes/methods later than Java 8 are used accidentally -->
                            <release>8</release>
                            <!-- exclude module-info.java from the compilation, as it is unsupported by Java 8 -->
                            <excludes>
                                <exclude>module-info.java</exclude>
                            </excludes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>