Skip to content

Commit

Permalink
Issue #227: Incompatible with the configuration cache (#228)
Browse files Browse the repository at this point in the history
* Add a few failing tests

* Bump minimum Gradle version to 6.8.3 to allow for configuration cache compatibility and to avoid bugs with dependency resolution on Gradle 6.6, which is the first version that the config cache is introduced

* Remove ResolveRewriteDependenciesTask and fix one config cache problem w/ RewriteDryRunTask

* Fix another config cache problem w/ RewriteDryRunTask

* More work done; tests are still failing...

* Delete redundant 'resolve dependencies' task

* Use notCompatibleWithConfigurationCache on Gradle 7.4+

* Remove invalid comment

* Cleanup

* Extract common config-cache test into base test class

* Cleanup after merge conflict resolution

* Cleanup after merge conflict resolution

* Add `rewrite-java-21` to `knownRewriteDependencies`

---------

Co-authored-by: Tim te Beek <[email protected]>
  • Loading branch information
jbduncan and timtebeek authored Aug 3, 2024
1 parent a12a440 commit 07804ce
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,41 @@
package org.openrewrite.gradle;

import org.gradle.api.DefaultTask;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.options.Option;
import org.gradle.util.GradleVersion;

import javax.inject.Inject;
import java.io.File;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public abstract class AbstractRewriteTask extends DefaultTask {
protected ResolveRewriteDependenciesTask resolveDependenciesTask;
protected Provider<Set<File>> resolvedDependencies;
protected boolean dumpGcActivity;
protected GradleProjectParser gpp;
protected RewriteExtension extension;

protected AbstractRewriteTask() {
if (GradleVersion.current().compareTo(GradleVersion.version("7.4")) >= 0) {
notCompatibleWithConfigurationCache("org.openrewrite.rewrite needs to parse the whole project");
}
}

public <T extends AbstractRewriteTask> T setExtension(RewriteExtension extension) {
this.extension = extension;
//noinspection unchecked
return (T) this;
}

public <T extends AbstractRewriteTask> T setResolveDependenciesTask(ResolveRewriteDependenciesTask resolveDependenciesTask) {
this.resolveDependenciesTask = resolveDependenciesTask;
this.dependsOn(resolveDependenciesTask);
public <T extends AbstractRewriteTask> T setResolvedDependencies(Provider<Set<File>> resolvedDependencies) {
this.resolvedDependencies = resolvedDependencies;
//noinspection unchecked
return (T) this;
}
Expand All @@ -55,16 +65,25 @@ public boolean isDumpGcActivity() {
return dumpGcActivity;
}

@Inject
public ProjectLayout getProjectLayout() {
throw new AssertionError("unexpected; getProjectLayout() should be overridden by Gradle");
}

@Internal
protected <T extends GradleProjectParser> T getProjectParser() {
if (gpp == null) {
if (extension == null) {
throw new IllegalArgumentException("Must configure extension");
}
if (resolveDependenciesTask == null) {
throw new IllegalArgumentException("Must configure resolveDependenciesTask");
if (resolvedDependencies == null) {
throw new IllegalArgumentException("Must configure resolvedDependencies");
}
Set<File> deps = resolvedDependencies.getOrNull();
if (deps == null) {
deps = Collections.emptySet();
}
Set<Path> classpath = resolveDependenciesTask.getResolvedDependencies().stream()
Set<Path> classpath = deps.stream()
.map(File::toPath)
.collect(Collectors.toSet());
gpp = new DelegatingProjectParser(getProject(), extension, classpath);
Expand Down

This file was deleted.

17 changes: 11 additions & 6 deletions plugin/src/main/java/org/openrewrite/gradle/RewriteDryRunTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,22 @@
import org.gradle.api.tasks.TaskAction;

import javax.inject.Inject;
import java.io.File;
import java.nio.file.Path;

public class RewriteDryRunTask extends AbstractRewriteTask {

private static final Logger logger = Logging.getLogger(RewriteDryRunTask.class);

// This must return File, rather than Path.
// On Gradle 4.0 annotating something returning a Path with @OutputFile triggers a bug that deadlocks Gradle
@OutputFile
public File getReportPath() {
return getProject().getLayout().getBuildDirectory().get().getAsFile().toPath().resolve("reports").resolve("rewrite").resolve("rewrite.patch").toFile();
public Path getReportPath() {
return getProjectLayout()
.getBuildDirectory()
.get()
.getAsFile()
.toPath()
.resolve("reports")
.resolve("rewrite")
.resolve("rewrite.patch");
}

@Inject
Expand All @@ -44,6 +49,6 @@ public RewriteDryRunTask() {

@TaskAction
public void run() {
getProjectParser().dryRun(getReportPath().toPath(), dumpGcActivity, throwable -> logger.info("Error during rewrite dry run", throwable));
getProjectParser().dryRun(getReportPath(), dumpGcActivity, throwable -> logger.info("Error during rewrite dry run", throwable));
}
}
93 changes: 85 additions & 8 deletions plugin/src/main/java/org/openrewrite/gradle/RewritePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,31 @@
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.attributes.Bundling;
import org.gradle.api.attributes.Category;
import org.gradle.api.attributes.LibraryElements;
import org.gradle.api.attributes.Usage;
import org.gradle.api.attributes.java.TargetJvmEnvironment;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.plugins.quality.CheckstyleExtension;
import org.gradle.api.plugins.quality.CheckstylePlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSetContainer;

import java.io.File;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;

import static org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE;
import static org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE;

/**
* When applied to the root project of a multi-project build, applies to all subprojects.
Expand All @@ -41,6 +55,8 @@
@SuppressWarnings("unused")
public class RewritePlugin implements Plugin<Project> {

private Set<File> resolvedDependencies;

@Override
public void apply(Project project) {
boolean isRootProject = project == project.getRootProject();
Expand All @@ -56,25 +72,21 @@ public void apply(Project project) {
// Rewrite module dependencies put here will be available to all rewrite tasks
Configuration rewriteConf = project.getConfigurations().maybeCreate("rewrite");

// We use this method of task creation because it works on old versions of Gradle
// Don't replace with TaskContainer.register() (introduced in 4.9), or another overload of create() (introduced in 4.7)
ResolveRewriteDependenciesTask resolveRewriteDependenciesTask = project.getTasks().create("rewriteResolveDependencies", ResolveRewriteDependenciesTask.class)
.setExtension(extension)
.setConfiguration(rewriteConf);
Provider<Set<File>> resolvedDependenciesProvider = project.provider(() -> getResolvedDependencies(project, extension, rewriteConf));

RewriteRunTask rewriteRun = project.getTasks().create("rewriteRun", RewriteRunTask.class)
.setExtension(extension)
.setResolveDependenciesTask(resolveRewriteDependenciesTask);
.setResolvedDependencies(resolvedDependenciesProvider);
rewriteRun.dependsOn(rewriteConf);

RewriteDryRunTask rewriteDryRun = project.getTasks().create("rewriteDryRun", RewriteDryRunTask.class)
.setExtension(extension)
.setResolveDependenciesTask(resolveRewriteDependenciesTask);
.setResolvedDependencies(resolvedDependenciesProvider);
rewriteDryRun.dependsOn(rewriteConf);

RewriteDiscoverTask rewriteDiscover = project.getTasks().create("rewriteDiscover", RewriteDiscoverTask.class)
.setExtension(extension)
.setResolveDependenciesTask(resolveRewriteDependenciesTask);
.setResolvedDependencies(resolvedDependenciesProvider);
rewriteDiscover.dependsOn(rewriteConf);

if (isRootProject) {
Expand Down Expand Up @@ -138,4 +150,69 @@ private static void configureProject(Project project, RewriteExtension extension
}));
});
}

private Set<File> getResolvedDependencies(Project project, RewriteExtension extension, Configuration rewriteConf) {
if (resolvedDependencies == null) {
Dependency[] dependencies = Stream.concat(
knownRewriteDependencies(extension, project.getDependencies()),
rewriteConf.getDependencies().stream()
).toArray(Dependency[]::new);
// By using a detached configuration, we separate this dependency resolution from the rest of the project's
// configuration. This also means that Gradle has no criteria with which to select between variants of
// dependencies which expose differing capabilities. So those must be manually configured
Configuration detachedConf = project.getConfigurations().detachedConfiguration(dependencies);

try {
ObjectFactory objectFactory = project.getObjects();
detachedConf.attributes(attributes -> {
// Adapted from org.gradle.api.plugins.jvm.internal.DefaultJvmEcosystemAttributesDetails
attributes.attribute(Category.CATEGORY_ATTRIBUTE, objectFactory.named(Category.class, Category.LIBRARY));
attributes.attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.JAVA_RUNTIME));
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objectFactory.named(LibraryElements.class, LibraryElements.JAR));
attributes.attribute(BUNDLING_ATTRIBUTE, objectFactory.named(Bundling.class, Bundling.EXTERNAL));
try {
attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objectFactory.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
} catch (NoClassDefFoundError e) {
// Old versions of Gradle don't have the class TargetJvmEnvironment and that's OK, we can always
// try this attribute instead
attributes.attribute(Attribute.of("org.gradle.jvm.environment", String.class), "standard-jvm");
}
});
} catch (NoClassDefFoundError e) {
// Old versions of Gradle don't have all of these attributes and that's OK
}

resolvedDependencies = detachedConf.resolve();
}
return resolvedDependencies;
}

private static Stream<Dependency> knownRewriteDependencies(RewriteExtension extension, DependencyHandler deps) {
String rewriteVersion = extension.getRewriteVersion();
return Stream.of(
deps.create("org.openrewrite:rewrite-core:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-groovy:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-gradle:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-hcl:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-json:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-kotlin:" + extension.getRewriteKotlinVersion()),
deps.create("org.openrewrite:rewrite-java:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-java-21:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-java-17:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-java-11:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-java-8:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-maven:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-properties:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-protobuf:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-xml:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-yaml:" + rewriteVersion),
deps.create("org.openrewrite:rewrite-polyglot:" + extension.getRewritePolyglotVersion()),
deps.create("org.openrewrite.gradle.tooling:model:" + extension.getRewriteGradleModelVersion()),

// This is an optional dependency of rewrite-java needed when projects also apply the checkstyle plugin
deps.create("com.puppycrawl.tools:checkstyle:" + extension.getCheckstyleToolsVersion()),
deps.create("com.fasterxml.jackson.module:jackson-module-kotlin:" + extension.getJacksonModuleKotlinVersion()),
deps.create("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:" + extension.getJacksonModuleKotlinVersion())
);
}
}
Loading

0 comments on commit 07804ce

Please sign in to comment.