Skip to content

Commit

Permalink
Extract JVM/Gradle compatibility into standalone plugin-let
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Lacasse <[email protected]>
  • Loading branch information
lacasseio committed Jul 8, 2024
1 parent 0a2d94d commit d2b04ac
Show file tree
Hide file tree
Showing 33 changed files with 662 additions and 644 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ abstract class AbstractGradlePluginDevelopmentExtensionFunctionalTest extends Ab
}

def "does not change source/target compatibility if already configured when a minimum Gradle version is configured"() {
assumeFalse(Jvm.current.java11)
assumeFalse(Jvm.current.java9)

given:
makeSingleProject()
Expand All @@ -51,14 +51,44 @@ abstract class AbstractGradlePluginDevelopmentExtensionFunctionalTest extends Ab
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_1_9
targetCompatibility = JavaVersion.VERSION_1_9
}
tasks.register('verify') {
doLast {
assert java.sourceCompatibility.toString() == '${JavaVersion.JAVA_11}'
assert java.targetCompatibility.toString() == '${JavaVersion.JAVA_11}'
assert java.sourceCompatibility.toString() == '${JavaVersion.JAVA_9}'
assert java.targetCompatibility.toString() == '${JavaVersion.JAVA_9}'
assert tasks.compileJava.sourceCompatibility == '${JavaVersion.JAVA_9}'
assert tasks.compileJava.targetCompatibility == '${JavaVersion.JAVA_9}'
}
}
"""

expect:
succeeds('verify')
}

def "override if only source compatibility configured"() {
assumeFalse(Jvm.current.java9)

given:
makeSingleProject()
buildFile << """
gradlePlugin {
compatibility.minimumGradleVersion = '6.2.1'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_9
}
tasks.register('verify') {
doLast {
assert java.sourceCompatibility.toString() == '${JavaVersion.JAVA_9}'
assert java.targetCompatibility.toString() == '${JavaVersion.JAVA_9}'
assert tasks.compileJava.sourceCompatibility == '${JavaVersion.JAVA_9}'
assert tasks.compileJava.targetCompatibility == '${JavaVersion.JAVA_9}'
}
}
"""
Expand All @@ -68,20 +98,22 @@ abstract class AbstractGradlePluginDevelopmentExtensionFunctionalTest extends Ab
}

def "does not change source/target compatibility if already configured when a no minimum Gradle version is configured"() {
assumeFalse(Jvm.current.java11)
assumeFalse(Jvm.current.java9)

given:
makeSingleProject()
buildFile << """
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_1_9
targetCompatibility = JavaVersion.VERSION_1_9
}
tasks.register('verify') {
doLast {
assert java.sourceCompatibility.toString() == '${JavaVersion.JAVA_11}'
assert java.targetCompatibility.toString() == '${JavaVersion.JAVA_11}'
assert java.sourceCompatibility.toString() == '${JavaVersion.JAVA_9}'
assert java.targetCompatibility.toString() == '${JavaVersion.JAVA_9}'
assert tasks.compileJava.sourceCompatibility == '${JavaVersion.JAVA_9}'
assert tasks.compileJava.targetCompatibility == '${JavaVersion.JAVA_9}'
}
}
"""
Expand All @@ -98,6 +130,8 @@ abstract class AbstractGradlePluginDevelopmentExtensionFunctionalTest extends Ab
doLast {
assert java.sourceCompatibility.toString() == '${Jvm.current.javaSpecificationVersion}'
assert java.targetCompatibility.toString() == '${Jvm.current.javaSpecificationVersion}'
assert tasks.compileJava.sourceCompatibility == '${Jvm.current.javaSpecificationVersion}'
assert tasks.compileJava.targetCompatibility == '${Jvm.current.javaSpecificationVersion}'
}
}
"""
Expand All @@ -114,8 +148,8 @@ abstract class AbstractGradlePluginDevelopmentExtensionFunctionalTest extends Ab
gradlePlugin.compatibility.minimumGradleVersion = '${gradleVersion}'
tasks.register('verify') {
doLast {
assert java.sourceCompatibility.toString() == '${javaVersion}'
assert java.targetCompatibility.toString() == '${javaVersion}'
assert tasks.compileJava.sourceCompatibility == '${javaVersion}'
assert tasks.compileJava.targetCompatibility == '${javaVersion}'
}
}
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ abstract class AbstractGradlePluginDevelopmentFunctionalTestingFunctionalTest ex
jar("build/libs/gradle-plugin.jar").assertFileContent("META-INF/gradle-plugins/${componentUnderTest.pluginId}.properties", CoreMatchers.startsWith('implementation-class=com.example.BasicPlugin'))
}

def "has no self-resolving Gradle TestKit dependency"() {
def "has no self-resolving Gradle TestKit dependency if using minimum Gradle version"() {
given:
makeSingleProject()
componentUnderTest.writeToProject(testDirectory)
buildFile << """
gradlePlugin.compatibility.minimumGradleVersion = '6.8'
tasks.register('verify') {
doLast {
assert !configurations.functionalTestImplementation.dependencies.any { it instanceof ${SelfResolvingDependencyInternal.canonicalName} ? it.targetComponentId.displayName == 'Gradle TestKit' : false }
Expand All @@ -49,6 +50,22 @@ abstract class AbstractGradlePluginDevelopmentFunctionalTestingFunctionalTest ex
succeeds('verify')
}

def "use self-resolving Gradle TestKit dependency if no minimum Gradle version"() {
given:
makeSingleProject()
componentUnderTest.writeToProject(testDirectory)
buildFile << """
tasks.register('verify') {
doLast {
assert configurations.functionalTestImplementation.dependencies.any { it instanceof ${SelfResolvingDependencyInternal.canonicalName} ? it.targetComponentId.displayName == 'Gradle TestKit' : false }
}
}
"""

expect:
succeeds('verify')
}

protected DefaultTestExecutionResult getTestResult() {
new DefaultTestExecutionResult(testDirectory, 'build', '', '', 'functionalTest')
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package dev.gradleplugins.internal;

import org.gradle.api.Action;
import org.gradle.api.Named;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.NamedDomainObjectSet;
import org.gradle.api.Project;
import org.gradle.api.Transformer;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.SourceSet;
import org.gradle.util.GradleVersion;

import javax.inject.Inject;

import static dev.gradleplugins.GradlePluginDevelopmentDependencyExtension.GRADLE_API_LOCAL_VERSION;

public abstract class GradleCompatibilities {
public abstract Property<String> getMinimumGradleVersion();
public abstract Property<String> getGradleApiVersion();

/*private*/ static abstract /*final*/ class Rule implements org.gradle.api.Plugin<Project> {
@Inject
public Rule() {}

@Override
public void apply(Project project) {
final GradleCompatibilitiesExtension extension = project.getExtensions().create("$gradleCompatibilities", GradleCompatibilitiesExtension.class);
extension.configureEach(new Action<GradleCompatibilities>() {
@Override
public void execute(GradleCompatibilities it) {
// For now, we will not allow to finalize the value just yet because of the override for 1.x series
// See GradlePluginDevelopmentCompatibilityExtensionRule
//it.getMinimumGradleVersion().finalizeValueOnRead();

it.getGradleApiVersion().convention(it.getMinimumGradleVersion().map(toLocalIfGradleSnapshotVersion()));
it.getGradleApiVersion().finalizeValueOnRead();
}

private /*static*/ Transformer<String, String> toLocalIfGradleSnapshotVersion() {
return it -> {
if (GradleVersion.version(it).isSnapshot()) {
return GRADLE_API_LOCAL_VERSION;
}
return it;
};
}
});
}
}

public interface ForSourceSetExtension {
NamedDomainObjectProvider<SourceSetGradleCompatibilities> forSourceSet(SourceSet sourceSet);
void configureEach(Action<? super SourceSetGradleCompatibilities> action);

abstract class SourceSetGradleCompatibilities extends GradleCompatibilities {
public abstract SourceSet getSourceSet();
}
}

/*private*/ static abstract /*final*/ class GradleCompatibilitiesExtension implements ForSourceSetExtension {
private final ObjectFactory objects;
private final NamedDomainObjectSet<SourceSetGradleCompatibilities> compatibilities;

@Inject
public GradleCompatibilitiesExtension(ObjectFactory objects) {
this.objects = objects;
this.compatibilities = objects.namedDomainObjectSet(SourceSetGradleCompatibilities.class);
}

public NamedDomainObjectProvider<SourceSetGradleCompatibilities> forSourceSet(SourceSet sourceSet) {
if (compatibilities.findByName(sourceSet.getName()) == null) {
compatibilities.add(objects.newInstance(DefaultGradleCompatibility.class, sourceSet));
}
return compatibilities.named(sourceSet.getName());
}

@Override
public void configureEach(Action<? super SourceSetGradleCompatibilities> action) {
compatibilities.configureEach(action);
}
}

/*private*/ static abstract /*final*/ class DefaultGradleCompatibility extends ForSourceSetExtension.SourceSetGradleCompatibilities implements Named {
private final SourceSet sourceSet;

@Inject
public DefaultGradleCompatibility(SourceSet sourceSet) {
this.sourceSet = sourceSet;
}

@Override
public String getName() {
return sourceSet.getName();
}

@Override
public SourceSet getSourceSet() {
return sourceSet;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package dev.gradleplugins.internal;

import org.gradle.api.Action;
import org.gradle.api.JavaVersion;
import org.gradle.api.Named;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.NamedDomainObjectSet;
import org.gradle.api.Project;
import org.gradle.api.Transformer;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile;

import javax.inject.Inject;
import java.util.function.Consumer;
import java.util.function.Supplier;

public abstract class JvmCompatibilities {
public abstract Property<JavaVersion> getSourceCompatibility();
public abstract Property<JavaVersion> getTargetCompatibility();

public static abstract class ForProjectExtension extends JvmCompatibilities {}

/*private*/ static abstract /*final*/ class ProjectJvmCompatibilitiesExtension extends ForProjectExtension {
@Inject
public ProjectJvmCompatibilitiesExtension(Project project) {
JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class);

getTargetCompatibility().convention(project.provider(java::getTargetCompatibility).map(toFinalValue(java::getTargetCompatibility, java::setTargetCompatibility, it -> {
// Important to restore the normla behaviour
// Normally, targetCompatibility derived from sourceCompatibility when not set
final JavaVersion sourceCompatibility = java.getSourceCompatibility();
if (!sourceCompatibility.equals(JavaVersion.VERSION_1_1)) {
java.setTargetCompatibility(sourceCompatibility);
} else {
java.setTargetCompatibility(it);
}
})).orElse(getSourceCompatibility()));
getTargetCompatibility().finalizeValueOnRead();
getTargetCompatibility().disallowChanges();
project.afterEvaluate(__ -> getTargetCompatibility().finalizeValue());

getSourceCompatibility().convention(project.provider(java::getSourceCompatibility).map(toFinalValue(java::getSourceCompatibility, java::setSourceCompatibility, it -> {
// Important to restore the normal behaviour
java.setSourceCompatibility(it);
})));
getSourceCompatibility().finalizeValueOnRead();
getSourceCompatibility().disallowChanges();
project.afterEvaluate(__ -> getSourceCompatibility().finalizeValue());
}

private Transformer<JavaVersion, JavaVersion> toFinalValue(Supplier<JavaVersion> getter, Consumer<JavaVersion> setter, Action<? super JavaVersion> restoreAction) {
final JavaVersion originalValue = getter.get();
setter.accept(JavaVersion.VERSION_1_1);
return it -> {
if (it.equals(JavaVersion.VERSION_1_1)) {
restoreAction.execute(originalValue);
return null;
} else {
return it;
}
};
}
}

/*private*/ static abstract /*final*/ class Rule implements org.gradle.api.Plugin<Project> {
@Inject
public Rule() {}

@Override
public void apply(Project project) {
final ProjectJvmCompatibilitiesExtension jvmCompatibilities = project.getExtensions().create("$jvmProjectCompatibilities", ProjectJvmCompatibilitiesExtension.class, project);

final SourceSetJvmCompatibilitiesExtension extension = project.getExtensions().create("$jvmCompatibilities", SourceSetJvmCompatibilitiesExtension.class);

extension.configureEach(it -> {
it.getSourceCompatibility().convention(jvmCompatibilities.getSourceCompatibility());
it.getTargetCompatibility().convention(jvmCompatibilities.getTargetCompatibility()
.orElse(it.getSourceCompatibility()));
});

extension.configureEach(it -> {
project.getTasks().named(it.getSourceSet().getCompileJavaTaskName(), JavaCompile.class).configure(task -> {
task.getConventionMapping().map("sourceCompatibility", () -> it.getSourceCompatibility().getOrElse(JavaVersion.current()).toString());
task.getConventionMapping().map("targetCompatibility", () -> it.getTargetCompatibility().getOrElse(JavaVersion.current()).toString());
// TODO: Gradle 6.7+ configure the javaCompiler property
});

// TODO: Configure test task (if available)
});
}
}

public interface ForSourceSetExtension {
NamedDomainObjectProvider<SourceSetJvmCompatibilities> forSourceSet(SourceSet sourceSet);
void configureEach(Action<? super SourceSetJvmCompatibilities> action);

abstract class SourceSetJvmCompatibilities extends JvmCompatibilities {
public abstract SourceSet getSourceSet();
}
}

/*private*/ static abstract /*final*/ class SourceSetJvmCompatibilitiesExtension implements ForSourceSetExtension {
private final ObjectFactory objects;
private final NamedDomainObjectSet<SourceSetJvmCompatibilities> compatibilities;

@Inject
public SourceSetJvmCompatibilitiesExtension(ObjectFactory objects) {
this.objects = objects;
this.compatibilities = objects.namedDomainObjectSet(SourceSetJvmCompatibilities.class);
}

public NamedDomainObjectProvider<SourceSetJvmCompatibilities> forSourceSet(SourceSet sourceSet) {
if (compatibilities.findByName(sourceSet.getName()) == null) {
compatibilities.add(objects.newInstance(DefaultSourceSetJvmCompatibilities.class, sourceSet));
}
return compatibilities.named(sourceSet.getName());
}

@Override
public void configureEach(Action<? super SourceSetJvmCompatibilities> action) {
compatibilities.configureEach(action);
}
}

/*private*/ static abstract /*final*/ class DefaultSourceSetJvmCompatibilities extends ForSourceSetExtension.SourceSetJvmCompatibilities implements Named {
private final SourceSet sourceSet;

@Inject
public DefaultSourceSetJvmCompatibilities(SourceSet sourceSet) {
this.sourceSet = sourceSet;
}

@Override
public String getName() {
return sourceSet.getName();
}

public SourceSet getSourceSet() {
return sourceSet;
}
}
}
Loading

0 comments on commit d2b04ac

Please sign in to comment.