Skip to content
Permalink
Browse files

java9-modularity#72: support for "modularity" project extension

via ModularityExtension interface
  • Loading branch information...
tlinkowski committed Mar 17, 2019
1 parent 81d5353 commit dd341b22ff2abd4e0ac4bc8b07f77a4ee06ca958
@@ -448,13 +448,54 @@ patchModules.config = [
Compilation
===

Compilation to a specific Java release
----

For Java releases 6-8, see [Separate compilation of `module-info.java`](#separate-compilation-of-module-infojava).

For Java releases 9+, this plugin provides an easy way to set the `--release` option of the `compileJava` task
by means of its [`modularity.standardJavaRelease`][ModularityExtension] function.

Separate compilation of `module-info.java`
----

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

Typically, this feature would be used by libraries which target JDK 6-8 but want to make the most of JPMS by:
- providing `module-info.class` for consumers who put the library on module path,
- compiling `module-info.java` against the remaining classes of this module and against other modules (which provides
better encapsulation and prevents split packages).

This plugin provides an easy way to do exactly that by means of its
[`modularity.mixedJavaRelease`][ModularityExtension] function, which implicitly sets
`compileJava.compileModuleInfoSeparately` and configures `--release` compiler options.

For example, if your library targets JDK 8, and you want your `module-info.class` to be compatible with JDK 9
(which is the default), put the following line in your `build.gradle(.kts)`:

<details open>
<summary>Groovy DSL</summary>

```groovy
modularity.mixedJavaRelease 8
```

</details>
<details>
<summary>Kotlin DSL</summary>

```kotlin
modularity.mixedJavaRelease(8)
```

</details>

Note that `modularity.mixedJavaRelease` does *not* configure a
[multi-release JAR](https://docs.oracle.com/javase/9/docs/specs/jar/jar.html#Multi-release)
(in other words, `module-info.class` remains in the root directory of the JAR).

Limitations
===

@@ -477,3 +518,6 @@ Contributions are very much welcome.
Please open a Pull Request with your changes.
Make sure to rebase before creating the PR so that the PR only contains your changes, this makes the review process much easier.
Again, bonus points for providing tests for your changes.


[ModularityExtension]: src/main/java/org/javamodularity/moduleplugin/extensions/ModularityExtension.java
@@ -4,6 +4,8 @@
import org.gradle.api.Project;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.plugins.JavaPlugin;
import org.javamodularity.moduleplugin.extensions.ModularityExtension;
import org.javamodularity.moduleplugin.extensions.ModularityExtensionImpl;
import org.javamodularity.moduleplugin.tasks.*;

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

new CompileTask(project).configureCompileJava();
new CompileModuleInfoTask(project).configureCompileModuleInfoJava();
@@ -0,0 +1,49 @@
package org.javamodularity.moduleplugin.extensions;

/**
* A project-wide extension that provides the most common modularity-related actions.
*
* @see ModularityExtensionImpl
*/
public interface ModularityExtension {

//region JAVA RELEASE

/**
* Calling this method results in all Java classes being compiled to Java release 9+ (as given by the
* {@code mainJavaRelease} parameter).
* <p>
* See details about the {@code --release} option
* <a href="https://docs.oracle.com/en/java/javase/11/tools/javac.html">here</a>.
*
* @param mainJavaRelease value for the {@code --release} option of {@code compileJava} task (allowed range: 9+)
*/
void standardJavaRelease(int mainJavaRelease);

/**
* Calling this method results in all Java classes being compiled to Java release 6-8 (as given by the
* {@code mainJavaRelease} parameter), with the exception of {@code module-info.java} being compiled to
* Java release 9.
*
* @param mainJavaRelease value for the {@code --release} option of {@code compileJava} task (allowed range: 6-8)
*/
default void mixedJavaRelease(int mainJavaRelease) {
mixedJavaRelease(mainJavaRelease, 9);
}

/**
* Calling this method results in all Java classes being compiled to Java release 6-8 (as given by the
* {@code mainJavaRelease} parameter), with the exception of {@code module-info.java} being compiled to
* Java release 9+ (as given by the {@code moduleInfoJavaRelease} parameter).
* <p>
* See details about the {@code --release} option
* <a href="https://docs.oracle.com/en/java/javase/11/tools/javac.html">here</a>.
*
* @param mainJavaRelease value for the {@code --release} option of {@code compileJava} task
* (allowed range: 6-8)
* @param moduleInfoJavaRelease value for the {@code --release} option of {@code compileModuleInfoJava} task
* (allowed range: 9+)
*/
void mixedJavaRelease(int mainJavaRelease, int moduleInfoJavaRelease);
//endregion
}
@@ -0,0 +1,97 @@
package org.javamodularity.moduleplugin.extensions;

import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.compile.JavaCompile;
import org.javamodularity.moduleplugin.JavaProjectHelper;

import java.util.List;

public class ModularityExtensionImpl implements ModularityExtension {

private final Project project;

public ModularityExtensionImpl(Project project) {
this.project = project;
}

//region JAVA RELEASE

//region STANDARD JAVA RELEASE
@Override
public void standardJavaRelease(int mainJavaRelease) {
if (mainJavaRelease < 9) {
throw new IllegalArgumentException(String.format(
"Invalid main --release value: %d. Use 'mixedJavaRelease' instead.", mainJavaRelease
));
}
project.afterEvaluate(p -> configureStandardJavaRelease(mainJavaRelease));
}

private void configureStandardJavaRelease(int mainJavaRelease) {
JavaCompile compileJava = helper().compileJavaTask(JavaPlugin.COMPILE_JAVA_TASK_NAME);
setJavaRelease(compileJava, mainJavaRelease);
}
//endregion

//region MIXED JAVA RELEASE
@Override
public void mixedJavaRelease(int mainJavaRelease, int moduleInfoJavaRelease) {
validateMixedJavaReleaseArgs(mainJavaRelease, moduleInfoJavaRelease);

CompileModuleOptions moduleOptions = helper().compileJavaTask(JavaPlugin.COMPILE_JAVA_TASK_NAME)
.getExtensions().getByType(CompileModuleOptions.class);
moduleOptions.setCompileModuleInfoSeparately(true);

project.afterEvaluate(p -> configureMixedJavaRelease(mainJavaRelease, moduleInfoJavaRelease));
}

private static void validateMixedJavaReleaseArgs(int mainJavaRelease, int moduleInfoJavaRelease) {
if (mainJavaRelease < 6) {
throw new IllegalArgumentException("Invalid main --release value: " + mainJavaRelease);
}
if (mainJavaRelease > 8) {
throw new IllegalArgumentException(String.format(
"Invalid main --release value: %d. Use 'standardJavaRelease' instead.", mainJavaRelease
));
}
if (moduleInfoJavaRelease < 9) {
throw new IllegalArgumentException("Invalid module-info --release value: " + moduleInfoJavaRelease);
}
}

private void configureMixedJavaRelease(int mainJavaRelease, int moduleInfoJavaRelease) {
var compileJava = helper().compileJavaTask(JavaPlugin.COMPILE_JAVA_TASK_NAME);
setJavaRelease(compileJava, mainJavaRelease);

var compileModuleInfoJava = helper().compileJavaTask(CompileModuleOptions.COMPILE_MODULE_INFO_TASK_NAME);
setJavaRelease(compileModuleInfoJava, moduleInfoJavaRelease);
}
//endregion

// TODO: Remove this method when Gradle supports it natively: https://github.com/gradle/gradle/issues/2510
private void setJavaRelease(JavaCompile javaCompile, int javaRelease) {
String currentJavaVersion = JavaVersion.current().toString();
if (!javaCompile.getSourceCompatibility().equals(currentJavaVersion)) {
throw new IllegalStateException("sourceCompatibility should not be set together with --release option");
}
if (!javaCompile.getTargetCompatibility().equals(currentJavaVersion)) {
throw new IllegalStateException("targetCompatibility should not be set together with --release option");
}

List<String> compilerArgs = javaCompile.getOptions().getCompilerArgs();
if (compilerArgs.contains("--release")) {
throw new IllegalStateException("--release option is already set in compiler args");
}

compilerArgs.add("--release");
compilerArgs.add(String.valueOf(javaRelease));
}
//endregion

private JavaProjectHelper helper() {
return new JavaProjectHelper(project);
}

}
@@ -18,6 +18,9 @@ tasks {
}
}
}

modularity {
}
//endregion

val compileKotlin: KotlinCompile by tasks
@@ -16,4 +16,7 @@ test {
runOnClasspath = false
}
}

modularity {
}
//endregion

0 comments on commit dd341b2

Please sign in to comment.
You can’t perform that action at this time.