Skip to content

Commit dd341b2

Browse files
committed
java9-modularity#72: support for "modularity" project extension
via ModularityExtension interface
1 parent 81d5353 commit dd341b2

6 files changed

Lines changed: 199 additions & 0 deletions

File tree

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,13 +448,54 @@ patchModules.config = [
448448
Compilation
449449
===
450450

451+
Compilation to a specific Java release
452+
----
453+
454+
For Java releases 6-8, see [Separate compilation of `module-info.java`](#separate-compilation-of-module-infojava).
455+
456+
For Java releases 9+, this plugin provides an easy way to set the `--release` option of the `compileJava` task
457+
by means of its [`modularity.standardJavaRelease`][ModularityExtension] function.
458+
451459
Separate compilation of `module-info.java`
452460
----
453461

454462
If you need to compile the main `module-info.java` separately from the rest of `src/main/java`
455463
files, you can enable `compileModuleInfoSeparately` option on `compileJava` task. It will exclude `module-info.java`
456464
from `compileJava` and introduce a dedicated `compileModuleInfoJava` task.
457465

466+
Typically, this feature would be used by libraries which target JDK 6-8 but want to make the most of JPMS by:
467+
- providing `module-info.class` for consumers who put the library on module path,
468+
- compiling `module-info.java` against the remaining classes of this module and against other modules (which provides
469+
better encapsulation and prevents split packages).
470+
471+
This plugin provides an easy way to do exactly that by means of its
472+
[`modularity.mixedJavaRelease`][ModularityExtension] function, which implicitly sets
473+
`compileJava.compileModuleInfoSeparately` and configures `--release` compiler options.
474+
475+
For example, if your library targets JDK 8, and you want your `module-info.class` to be compatible with JDK 9
476+
(which is the default), put the following line in your `build.gradle(.kts)`:
477+
478+
<details open>
479+
<summary>Groovy DSL</summary>
480+
481+
```groovy
482+
modularity.mixedJavaRelease 8
483+
```
484+
485+
</details>
486+
<details>
487+
<summary>Kotlin DSL</summary>
488+
489+
```kotlin
490+
modularity.mixedJavaRelease(8)
491+
```
492+
493+
</details>
494+
495+
Note that `modularity.mixedJavaRelease` does *not* configure a
496+
[multi-release JAR](https://docs.oracle.com/javase/9/docs/specs/jar/jar.html#Multi-release)
497+
(in other words, `module-info.class` remains in the root directory of the JAR).
498+
458499
Limitations
459500
===
460501

@@ -477,3 +518,6 @@ Contributions are very much welcome.
477518
Please open a Pull Request with your changes.
478519
Make sure to rebase before creating the PR so that the PR only contains your changes, this makes the review process much easier.
479520
Again, bonus points for providing tests for your changes.
521+
522+
523+
[ModularityExtension]: src/main/java/org/javamodularity/moduleplugin/extensions/ModularityExtension.java

src/main/java/org/javamodularity/moduleplugin/ModuleSystemPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import org.gradle.api.Project;
55
import org.gradle.api.plugins.ExtensionContainer;
66
import org.gradle.api.plugins.JavaPlugin;
7+
import org.javamodularity.moduleplugin.extensions.ModularityExtension;
8+
import org.javamodularity.moduleplugin.extensions.ModularityExtensionImpl;
79
import org.javamodularity.moduleplugin.tasks.*;
810

911
public class ModuleSystemPlugin implements Plugin<Project> {
@@ -18,6 +20,7 @@ private void configureModularity(Project project, String moduleName) {
1820
ExtensionContainer extensions = project.getExtensions();
1921
extensions.add("moduleName", moduleName);
2022
extensions.create("patchModules", PatchModuleExtension.class);
23+
extensions.create(ModularityExtension.class, "modularity", ModularityExtensionImpl.class, project);
2124

2225
new CompileTask(project).configureCompileJava();
2326
new CompileModuleInfoTask(project).configureCompileModuleInfoJava();
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.javamodularity.moduleplugin.extensions;
2+
3+
/**
4+
* A project-wide extension that provides the most common modularity-related actions.
5+
*
6+
* @see ModularityExtensionImpl
7+
*/
8+
public interface ModularityExtension {
9+
10+
//region JAVA RELEASE
11+
12+
/**
13+
* Calling this method results in all Java classes being compiled to Java release 9+ (as given by the
14+
* {@code mainJavaRelease} parameter).
15+
* <p>
16+
* See details about the {@code --release} option
17+
* <a href="https://docs.oracle.com/en/java/javase/11/tools/javac.html">here</a>.
18+
*
19+
* @param mainJavaRelease value for the {@code --release} option of {@code compileJava} task (allowed range: 9+)
20+
*/
21+
void standardJavaRelease(int mainJavaRelease);
22+
23+
/**
24+
* Calling this method results in all Java classes being compiled to Java release 6-8 (as given by the
25+
* {@code mainJavaRelease} parameter), with the exception of {@code module-info.java} being compiled to
26+
* Java release 9.
27+
*
28+
* @param mainJavaRelease value for the {@code --release} option of {@code compileJava} task (allowed range: 6-8)
29+
*/
30+
default void mixedJavaRelease(int mainJavaRelease) {
31+
mixedJavaRelease(mainJavaRelease, 9);
32+
}
33+
34+
/**
35+
* Calling this method results in all Java classes being compiled to Java release 6-8 (as given by the
36+
* {@code mainJavaRelease} parameter), with the exception of {@code module-info.java} being compiled to
37+
* Java release 9+ (as given by the {@code moduleInfoJavaRelease} parameter).
38+
* <p>
39+
* See details about the {@code --release} option
40+
* <a href="https://docs.oracle.com/en/java/javase/11/tools/javac.html">here</a>.
41+
*
42+
* @param mainJavaRelease value for the {@code --release} option of {@code compileJava} task
43+
* (allowed range: 6-8)
44+
* @param moduleInfoJavaRelease value for the {@code --release} option of {@code compileModuleInfoJava} task
45+
* (allowed range: 9+)
46+
*/
47+
void mixedJavaRelease(int mainJavaRelease, int moduleInfoJavaRelease);
48+
//endregion
49+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.javamodularity.moduleplugin.extensions;
2+
3+
import org.gradle.api.JavaVersion;
4+
import org.gradle.api.Project;
5+
import org.gradle.api.plugins.JavaPlugin;
6+
import org.gradle.api.tasks.compile.JavaCompile;
7+
import org.javamodularity.moduleplugin.JavaProjectHelper;
8+
9+
import java.util.List;
10+
11+
public class ModularityExtensionImpl implements ModularityExtension {
12+
13+
private final Project project;
14+
15+
public ModularityExtensionImpl(Project project) {
16+
this.project = project;
17+
}
18+
19+
//region JAVA RELEASE
20+
21+
//region STANDARD JAVA RELEASE
22+
@Override
23+
public void standardJavaRelease(int mainJavaRelease) {
24+
if (mainJavaRelease < 9) {
25+
throw new IllegalArgumentException(String.format(
26+
"Invalid main --release value: %d. Use 'mixedJavaRelease' instead.", mainJavaRelease
27+
));
28+
}
29+
project.afterEvaluate(p -> configureStandardJavaRelease(mainJavaRelease));
30+
}
31+
32+
private void configureStandardJavaRelease(int mainJavaRelease) {
33+
JavaCompile compileJava = helper().compileJavaTask(JavaPlugin.COMPILE_JAVA_TASK_NAME);
34+
setJavaRelease(compileJava, mainJavaRelease);
35+
}
36+
//endregion
37+
38+
//region MIXED JAVA RELEASE
39+
@Override
40+
public void mixedJavaRelease(int mainJavaRelease, int moduleInfoJavaRelease) {
41+
validateMixedJavaReleaseArgs(mainJavaRelease, moduleInfoJavaRelease);
42+
43+
CompileModuleOptions moduleOptions = helper().compileJavaTask(JavaPlugin.COMPILE_JAVA_TASK_NAME)
44+
.getExtensions().getByType(CompileModuleOptions.class);
45+
moduleOptions.setCompileModuleInfoSeparately(true);
46+
47+
project.afterEvaluate(p -> configureMixedJavaRelease(mainJavaRelease, moduleInfoJavaRelease));
48+
}
49+
50+
private static void validateMixedJavaReleaseArgs(int mainJavaRelease, int moduleInfoJavaRelease) {
51+
if (mainJavaRelease < 6) {
52+
throw new IllegalArgumentException("Invalid main --release value: " + mainJavaRelease);
53+
}
54+
if (mainJavaRelease > 8) {
55+
throw new IllegalArgumentException(String.format(
56+
"Invalid main --release value: %d. Use 'standardJavaRelease' instead.", mainJavaRelease
57+
));
58+
}
59+
if (moduleInfoJavaRelease < 9) {
60+
throw new IllegalArgumentException("Invalid module-info --release value: " + moduleInfoJavaRelease);
61+
}
62+
}
63+
64+
private void configureMixedJavaRelease(int mainJavaRelease, int moduleInfoJavaRelease) {
65+
var compileJava = helper().compileJavaTask(JavaPlugin.COMPILE_JAVA_TASK_NAME);
66+
setJavaRelease(compileJava, mainJavaRelease);
67+
68+
var compileModuleInfoJava = helper().compileJavaTask(CompileModuleOptions.COMPILE_MODULE_INFO_TASK_NAME);
69+
setJavaRelease(compileModuleInfoJava, moduleInfoJavaRelease);
70+
}
71+
//endregion
72+
73+
// TODO: Remove this method when Gradle supports it natively: https://github.com/gradle/gradle/issues/2510
74+
private void setJavaRelease(JavaCompile javaCompile, int javaRelease) {
75+
String currentJavaVersion = JavaVersion.current().toString();
76+
if (!javaCompile.getSourceCompatibility().equals(currentJavaVersion)) {
77+
throw new IllegalStateException("sourceCompatibility should not be set together with --release option");
78+
}
79+
if (!javaCompile.getTargetCompatibility().equals(currentJavaVersion)) {
80+
throw new IllegalStateException("targetCompatibility should not be set together with --release option");
81+
}
82+
83+
List<String> compilerArgs = javaCompile.getOptions().getCompilerArgs();
84+
if (compilerArgs.contains("--release")) {
85+
throw new IllegalStateException("--release option is already set in compiler args");
86+
}
87+
88+
compilerArgs.add("--release");
89+
compilerArgs.add(String.valueOf(javaRelease));
90+
}
91+
//endregion
92+
93+
private JavaProjectHelper helper() {
94+
return new JavaProjectHelper(project);
95+
}
96+
97+
}

test-project-kotlin/greeter.api/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ tasks {
1818
}
1919
}
2020
}
21+
22+
modularity {
23+
}
2124
//endregion
2225

2326
val compileKotlin: KotlinCompile by tasks

test-project/greeter.api/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ test {
1616
runOnClasspath = false
1717
}
1818
}
19+
20+
modularity {
21+
}
1922
//endregion

0 commit comments

Comments
 (0)