Skip to content

Commit 6f339bb

Browse files
committed
Merge branch 'big-andy-coates-issue-212'
2 parents c778dc2 + f5101fc commit 6f339bb

File tree

46 files changed

+698
-22
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+698
-22
lines changed

src/main/java/org/javamodularity/moduleplugin/tasks/CompileJavaTaskMutator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
import org.gradle.api.file.FileCollection;
55
import org.gradle.api.logging.Logger;
66
import org.gradle.api.logging.Logging;
7-
import org.gradle.api.tasks.compile.AbstractCompile;
87
import org.gradle.api.tasks.compile.JavaCompile;
98
import org.javamodularity.moduleplugin.JavaProjectHelper;
109
import org.javamodularity.moduleplugin.extensions.CompileModuleOptions;
1110
import org.javamodularity.moduleplugin.extensions.PatchModuleContainer;
1211
import org.javamodularity.moduleplugin.internal.MutatorHelper;
12+
import org.javamodularity.moduleplugin.tasks.MergeClassesHelper.CompileTaskWrapper;
1313

1414
import java.util.ArrayList;
1515
import java.util.List;
@@ -60,7 +60,7 @@ private List<String> buildCompilerArgs(JavaCompile javaCompile) {
6060
helper().modularityExtension().optionContainer().getPatchModuleContainer());
6161
String moduleName = helper().moduleName();
6262
new MergeClassesHelper(project).otherCompileTaskStream()
63-
.map(AbstractCompile::getDestinationDir)
63+
.map(CompileTaskWrapper::getDestinationDir)
6464
.forEach(dir -> patchModuleContainer.addDir(moduleName, dir.getAbsolutePath()));
6565

6666
// Keep only valid module-path entries (https://github.com/java9-modularity/gradle-modules-plugin/issues/190)

src/main/java/org/javamodularity/moduleplugin/tasks/MergeClassesHelper.java

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.javamodularity.moduleplugin.tasks;
22

33
import org.gradle.api.Project;
4+
import org.gradle.api.Task;
5+
import org.gradle.api.file.DirectoryProperty;
46
import org.gradle.api.file.DuplicatesStrategy;
57
import org.gradle.api.file.FileCollection;
68
import org.gradle.api.logging.Logger;
@@ -15,10 +17,13 @@
1517
import org.javamodularity.moduleplugin.internal.StreamHelper;
1618

1719
import java.io.File;
20+
import java.lang.reflect.InvocationTargetException;
21+
import java.lang.reflect.Method;
1822
import java.util.HashSet;
1923
import java.util.List;
2024
import java.util.Optional;
2125
import java.util.Set;
26+
import java.util.function.Supplier;
2227
import java.util.stream.Stream;
2328

2429
public class MergeClassesHelper {
@@ -38,10 +43,11 @@ public Project project() {
3843
return project;
3944
}
4045

41-
public Stream<AbstractCompile> otherCompileTaskStream() {
46+
public Stream<CompileTaskWrapper> otherCompileTaskStream() {
4247
return otherCompileTaskNameStream()
43-
.map(name -> helper().findTask(name, AbstractCompile.class))
44-
.flatMap(Optional::stream);
48+
.map(name -> helper().findTask(name, Task.class))
49+
.flatMap(Optional::stream)
50+
.map(this::toWrapper);
4551
}
4652

4753
private Stream<String> otherCompileTaskNameStream() {
@@ -52,16 +58,16 @@ private Stream<String> otherCompileTaskNameStream() {
5258
);
5359
}
5460

55-
public JavaCompile javaCompileTask() {
56-
return helper().task(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile.class);
61+
private CompileTaskWrapper javaCompileTask() {
62+
return toWrapper(helper().task(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile.class));
5763
}
5864

59-
public Stream<AbstractCompile> allCompileTaskStream() {
65+
public Stream<CompileTaskWrapper> allCompileTaskStream() {
6066
return Stream.concat(Stream.of(javaCompileTask()), otherCompileTaskStream());
6167
}
6268

6369
public boolean isMergeRequired() {
64-
return otherCompileTaskStream().anyMatch(task -> !task.getSource().isEmpty());
70+
return otherCompileTaskStream().anyMatch(CompileTaskWrapper::hasSource);
6571
}
6672

6773
public Sync createMergeClassesTask() {
@@ -84,12 +90,112 @@ public FileCollection getMergeAdjustedClasspath(FileCollection classpath) {
8490
}
8591

8692
Set<File> files = new HashSet<>(classpath.getFiles());
87-
allCompileTaskStream().map(AbstractCompile::getDestinationDir).forEach(files::remove);
93+
allCompileTaskStream().map(CompileTaskWrapper::getDestinationDir).forEach(files::remove);
8894
files.add(helper().getMergedDir());
8995
return project.files(files.toArray());
9096
}
9197

98+
private CompileTaskWrapper toWrapper(Task task) {
99+
return task instanceof AbstractCompile
100+
? new GradleTaskWrapper((AbstractCompile) task)
101+
: new ReflectionTaskWrapper(task);
102+
}
103+
92104
private JavaProjectHelper helper() {
93105
return new JavaProjectHelper(project);
94106
}
107+
108+
public interface CompileTaskWrapper {
109+
Task getTask();
110+
111+
File getDestinationDir();
112+
113+
boolean hasSource();
114+
}
115+
116+
private static final class GradleTaskWrapper implements CompileTaskWrapper {
117+
118+
private final AbstractCompile task;
119+
120+
GradleTaskWrapper(AbstractCompile task) {
121+
this.task = task;
122+
}
123+
124+
@Override
125+
public Task getTask() {
126+
return task;
127+
}
128+
129+
@Override
130+
public File getDestinationDir() {
131+
return task.getDestinationDir();
132+
}
133+
134+
@Override
135+
public boolean hasSource() {
136+
return !task.getSource().isEmpty();
137+
}
138+
}
139+
140+
private static final class ReflectionTaskWrapper implements CompileTaskWrapper {
141+
142+
private final Task task;
143+
private final Supplier<DirectoryProperty> destinationDir;
144+
private final Supplier<FileCollection> source;
145+
146+
ReflectionTaskWrapper(Task task) {
147+
this.task = task;
148+
this.destinationDir = supplier(
149+
task,
150+
"getDestinationDirectory",
151+
DirectoryProperty.class
152+
);
153+
this.source = supplier(
154+
task,
155+
"getSources",
156+
FileCollection.class
157+
);
158+
}
159+
160+
@Override
161+
public Task getTask() {
162+
return task;
163+
}
164+
165+
@Override
166+
public File getDestinationDir() {
167+
return destinationDir.get().getAsFile().getOrNull();
168+
}
169+
170+
@Override
171+
public boolean hasSource() {
172+
return !source.get().isEmpty();
173+
}
174+
175+
private static <T> Supplier<T> supplier(Task task, String getterName, Class<T> getterReturnType) {
176+
final Method m = getMethod(task, getterName);
177+
178+
return () -> {
179+
try {
180+
final Object result = m.invoke(task);
181+
return getterReturnType.cast(result);
182+
} catch (InvocationTargetException e) {
183+
if (e.getTargetException() instanceof RuntimeException) {
184+
throw (RuntimeException) e.getTargetException();
185+
}
186+
throw new RuntimeException(e.getTargetException());
187+
} catch (IllegalAccessException e) {
188+
throw new RuntimeException("Failed to invoke " + getterName + " on " + task.getClass(), e);
189+
}
190+
};
191+
}
192+
193+
private static Method getMethod(Task task, String name) {
194+
try {
195+
return task.getClass().getMethod(name);
196+
} catch (NoSuchMethodException e) {
197+
throw new IllegalStateException("Method " + name + " missing on " + task.getClass(), e);
198+
}
199+
}
200+
}
95201
}

src/main/java/org/javamodularity/moduleplugin/tasks/MergeClassesTask.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ public void configureMergeClasses() {
2626
public void configureMergeClassesAfterEvaluate() {
2727
var mergeClasses = mergeClassesHelper().createMergeClassesTask();
2828

29-
mergeClassesHelper().allCompileTaskStream().forEach(task -> {
29+
mergeClassesHelper().allCompileTaskStream().forEach(taskWrapper -> {
3030
List<String> modularTasks = List.of(JavaPlugin.COMPILE_JAVA_TASK_NAME, CompileModuleOptions.COMPILE_MODULE_INFO_TASK_NAME);
31-
if(modularTasks.contains(task.getName())) {
32-
mergeClasses.from(task.getDestinationDir());
31+
if(modularTasks.contains(taskWrapper.getTask().getName())) {
32+
mergeClasses.from(taskWrapper.getDestinationDir());
3333
} else {
34-
mergeClasses.from(task.getDestinationDir(), copySpec -> copySpec.exclude("**/module-info.class"));
34+
mergeClasses.from(taskWrapper.getDestinationDir(), copySpec -> copySpec.exclude("**/module-info.class"));
3535
}
36-
mergeClasses.dependsOn(task);
36+
mergeClasses.dependsOn(taskWrapper.getTask());
3737
});
3838
mergeClasses.into(helper().getMergedDir());
3939
mergeClasses.onlyIf(task -> mergeClassesHelper().isMergeRequired());

src/test/java/org/javamodularity/moduleplugin/ModulePluginSmokeTest.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void before() throws IOException {
3737
}
3838

3939
@CartesianProductTest(name = "smokeTest({arguments})")
40-
@CartesianValueSource(strings = {"test-project", "test-project-kotlin", "test-project-groovy"})
40+
@CartesianValueSource(strings = {"test-project", "test-project-kotlin-pre-1-7", "test-project-kotlin", "test-project-groovy"})
4141
@CartesianValueSource(strings = {"5.1", "5.6", "6.3", "6.4.1", "6.5.1", "6.8.3", "7.0", "7.2"})
4242
void smokeTest(String projectName, String gradleVersion) {
4343
LOGGER.lifecycle("Executing smokeTest of {} with Gradle {}", projectName, gradleVersion);
@@ -60,7 +60,7 @@ void smokeTest(String projectName, String gradleVersion) {
6060
}
6161

6262
@CartesianProductTest(name = "smokeTestRun({arguments})")
63-
@CartesianValueSource(strings = {"test-project", "test-project-kotlin", "test-project-groovy"})
63+
@CartesianValueSource(strings = {"test-project", "test-project-kotlin-pre-1-7", "test-project-kotlin", "test-project-groovy"})
6464
@CartesianValueSource(strings = {"5.1", "5.6", "6.3", "6.4.1", "6.5.1", "6.8.3", "7.0", "7.2"})
6565
void smokeTestRun(String projectName, String gradleVersion) {
6666
LOGGER.lifecycle("Executing smokeTestRun of {} with Gradle {}", projectName, gradleVersion);
@@ -158,7 +158,7 @@ private static void assertExpectedClassFileFormats(
158158
}
159159

160160
@CartesianProductTest(name = "smokeTestDist({arguments})")
161-
@CartesianValueSource(strings = {"test-project", "test-project-kotlin", "test-project-groovy"})
161+
@CartesianValueSource(strings = {"test-project", "test-project-kotlin-pre-1-7", "test-project-kotlin", "test-project-groovy"})
162162
@CartesianValueSource(strings = {"5.1", "5.6", "6.3", "6.4.1", "6.5.1", "6.8.3", "7.0", "7.2"})
163163
void smokeTestDist(String projectName, String gradleVersion) {
164164
LOGGER.lifecycle("Executing smokeTestDist of {} with Gradle {}", projectName, gradleVersion);
@@ -199,7 +199,7 @@ void smokeTestDist(String projectName, String gradleVersion) {
199199
}
200200

201201
@CartesianProductTest(name = "smokeTestRunDemo({arguments})")
202-
@CartesianValueSource(strings = {"test-project", "test-project-kotlin", "test-project-groovy"})
202+
@CartesianValueSource(strings = {"test-project", "test-project-kotlin-pre-1-7", "test-project-kotlin", "test-project-groovy"})
203203
@CartesianValueSource(strings = {"5.1", "5.6", "6.3", "6.4.1", "6.5.1", "6.8.3", "7.0", "7.2"})
204204
void smokeTestRunDemo(String projectName, String gradleVersion) {
205205
LOGGER.lifecycle("Executing smokeTestRunDemo of {} with Gradle {}", projectName, gradleVersion);
@@ -218,7 +218,7 @@ void smokeTestRunDemo(String projectName, String gradleVersion) {
218218
}
219219

220220
@CartesianProductTest(name = "smokeTestRunStartScripts({arguments})")
221-
@CartesianValueSource(strings = {"test-project", "test-project-kotlin", "test-project-groovy"})
221+
@CartesianValueSource(strings = {"test-project", "test-project-kotlin-pre-1-7", "test-project-kotlin", "test-project-groovy"})
222222
@CartesianValueSource(strings = {"5.1", "5.6", "6.3", "6.4.1", "6.5.1", "6.8.3", "7.0", "7.2"})
223223
void smokeTestRunStartScripts(String projectName, String gradleVersion) {
224224
LOGGER.lifecycle("Executing smokeTestRunScripts of {} with Gradle {}", projectName, gradleVersion);
@@ -248,7 +248,9 @@ private static void assertTasksSuccessful(BuildResult result, String subprojectN
248248
}
249249

250250
private static boolean checkCombination(String projectName, String gradleVersion) {
251-
if(projectName.equals("test-project-kotlin") && gradleVersion.compareTo("6.4") < 0) {
251+
final boolean kotlin_NotSupported = projectName.startsWith("test-project-kotlin") && gradleVersion.compareTo("6.4") < 0;
252+
final boolean kotlin1_7_NotSupported = projectName.equals("test-project-kotlin") && gradleVersion.compareTo("6.6") < 0;
253+
if (kotlin_NotSupported || kotlin1_7_NotSupported) {
252254
LOGGER.lifecycle("Unsupported combination: {} / Gradle {}. Test skipped", projectName, gradleVersion);
253255
return false;
254256
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Introduction
2+
===
3+
4+
This Kotlin test project can be used as a standalone test project to verify the published plugin.
5+
It is also used as an internal test project for testing unpublished plugin changes.
6+
7+
Standalone test product
8+
===
9+
To run this product as a standalone test product use this command (launched from `test-project-kotlin` directory):
10+
```
11+
../gradlew clean build
12+
```
13+
14+
It will use the most recent plugin version from Gradle maven repository to compile the test project with
15+
modules and run the unit tests.
16+
17+
Testing locally published plugin
18+
===
19+
20+
You can publish the plugin locally by running this command from the root directory:
21+
22+
`./gradlew publishToMavenLocal`
23+
24+
You can test the locally published plugin by running the following command from `test-project-kotlin` directory.
25+
26+
`../gradlew -c local_maven_settings.gradle clean build`
27+
28+
It will use the locally published version of the plugin to compile the test project with
29+
modules and run the unit tests.
30+
31+
32+
Internal test project
33+
===
34+
35+
This mode is enabled in `ModulePluginSmokeTest` by passing an extra parameter (`-c smoke_test_settings.gradle`).
36+
`smoke_test_settings.gradle` script configures plugin management for `build.gradle.kts` so that the plugin cannot be resolved from
37+
a Gradle plugin repository. Instead, it relies on the smoke test to make the plugin under development available
38+
to the test project by sharing a classpath (using Gradle TestKit).
39+
40+
__CAUTION:__ This approach won't work outside of the smoke test, it will break the build because the plugin jar won't be resolved.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2+
3+
plugins {
4+
kotlin("jvm") version "1.3.72" apply false
5+
id("org.javamodularity.moduleplugin") version "1.8.11" apply false
6+
}
7+
8+
subprojects {
9+
apply(plugin = "kotlin")
10+
apply(plugin = "org.javamodularity.moduleplugin")
11+
12+
//region https://docs.gradle.org/current/userguide/kotlin_dsl.html#using_kotlin_delegated_properties
13+
val test by tasks.existing(Test::class)
14+
val build by tasks
15+
val javadoc by tasks
16+
17+
val implementation by configurations
18+
val testImplementation by configurations
19+
val testRuntimeOnly by configurations
20+
21+
val jUnitVersion: String by project
22+
val jUnitPlatformVersion: String by project
23+
//endregion
24+
25+
//region KOTLIN
26+
tasks.withType<KotlinCompile> {
27+
kotlinOptions.jvmTarget = "1.8"
28+
}
29+
dependencies {
30+
implementation(kotlin("stdlib-jdk8"))
31+
}
32+
//endregion
33+
34+
repositories {
35+
mavenCentral()
36+
}
37+
38+
configure<org.javamodularity.moduleplugin.extensions.ModularityExtension> {
39+
improveEclipseClasspathFile()
40+
moduleVersion("1.2.3")
41+
}
42+
43+
test {
44+
useJUnitPlatform()
45+
46+
testLogging {
47+
events("PASSED", "FAILED", "SKIPPED", "STANDARD_OUT")
48+
}
49+
}
50+
51+
dependencies {
52+
testImplementation("org.junit.jupiter:junit-jupiter-api:$jUnitVersion")
53+
testImplementation("org.junit.jupiter:junit-jupiter-params:$jUnitVersion")
54+
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jUnitVersion")
55+
testRuntimeOnly("org.junit.platform:junit-platform-launcher:$jUnitPlatformVersion")
56+
}
57+
58+
// build.dependsOn(javadoc) // TODO: No public or protected classes found to document
59+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
jUnitVersion = 5.6.2
2+
jUnitPlatformVersion = 1.6.2
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import org.javamodularity.moduleplugin.extensions.CompileModuleOptions
2+
import org.javamodularity.moduleplugin.extensions.TestModuleOptions
3+
4+
//region NO-OP (DSL testing)
5+
tasks.compileJava {
6+
extensions.configure<CompileModuleOptions> {
7+
addModules = listOf()
8+
compileModuleInfoSeparately = false
9+
}
10+
}
11+
12+
tasks.test {
13+
extensions.configure<TestModuleOptions> {
14+
addModules = listOf()
15+
runOnClasspath = false
16+
}
17+
}
18+
19+
modularity {
20+
}
21+
//endregion

0 commit comments

Comments
 (0)