Skip to content

Commit

Permalink
java9-modularity#72: support for "modularity" project extension
Browse files Browse the repository at this point in the history
via ModularityExtension interface
  • Loading branch information
tlinkowski committed Mar 21, 2019
1 parent 81d5353 commit dd341b2
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 0 deletions.
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
===

Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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);
}

}
3 changes: 3 additions & 0 deletions test-project-kotlin/greeter.api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ tasks {
}
}
}

modularity {
}
//endregion

val compileKotlin: KotlinCompile by tasks
Expand Down
3 changes: 3 additions & 0 deletions test-project/greeter.api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ test {
runOnClasspath = false
}
}

modularity {
}
//endregion

0 comments on commit dd341b2

Please sign in to comment.