Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
package org.openrewrite.gradle;

import org.gradle.api.DefaultTask;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.options.Option;
Expand All @@ -30,16 +31,18 @@
import java.util.List;
import java.util.Set;

import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toSet;

public abstract class AbstractRewriteTask extends DefaultTask {
protected @Nullable Provider<Set<File>> resolvedDependencies;
protected boolean dumpGcActivity;
protected @Nullable GradleProjectParser gpp;
protected @Nullable RewriteExtension extension;
private final ConfigurableFileCollection resolvedDependencies;
private final ConfigurableFileCollection projectClasspath;

protected AbstractRewriteTask() {
resolvedDependencies = getProject().files();
projectClasspath = getProject().files();
if (GradleVersion.current().compareTo(GradleVersion.version("7.4")) >= 0) {
notCompatibleWithConfigurationCache("org.openrewrite.rewrite needs to parse the whole project");
}
Expand All @@ -51,10 +54,20 @@ public <T extends AbstractRewriteTask> T setExtension(RewriteExtension extension
return (T) this;
}

public <T extends AbstractRewriteTask> T setResolvedDependencies(Provider<Set<File>> resolvedDependencies) {
this.resolvedDependencies = resolvedDependencies;
//noinspection unchecked
return (T) this;
@Classpath
public ConfigurableFileCollection getResolvedDependencies() {
return resolvedDependencies;
}

/**
* The project's runtime classpaths, declared as an input so that Gradle
* knows to build dependent project jars before running rewrite tasks.
* This is separate from {@link #getResolvedDependencies()} to avoid polluting the
* rewrite recipe classpath with project dependencies.
*/
@Classpath
public ConfigurableFileCollection getProjectClasspath() {
return projectClasspath;
}

@Option(description = "Dump GC activity related to parsing.", option = "dumpGcActivity")
Expand All @@ -78,14 +91,7 @@ protected <T extends GradleProjectParser> T getProjectParser() {
if (extension == null) {
throw new IllegalArgumentException("Must configure extension");
}
if (resolvedDependencies == null) {
throw new IllegalArgumentException("Must configure resolvedDependencies");
}
Set<File> deps = resolvedDependencies.getOrNull();
if (deps == null) {
deps = emptySet();
}
Set<Path> classpath = deps.stream()
Set<Path> classpath = getResolvedDependencies().getFiles().stream()
.map(File::toPath)
.collect(toSet());
gpp = new DelegatingProjectParser(getProject(), extension, classpath);
Expand Down
96 changes: 50 additions & 46 deletions plugin/src/main/java/org/openrewrite/gradle/RewritePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@
import org.gradle.api.plugins.JvmEcosystemPlugin;
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 org.gradle.api.tasks.TaskProvider;
import org.jspecify.annotations.Nullable;

import java.io.File;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.Callable;

import static org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE;
import static org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE;
Expand All @@ -51,9 +50,6 @@
@SuppressWarnings("unused")
public class RewritePlugin implements Plugin<Project> {

@Nullable
private Set<File> resolvedDependencies;

@Override
public void apply(Project project) {
boolean isRootProject = project == project.getRootProject();
Expand All @@ -70,24 +66,31 @@ public void apply(Project project) {
Configuration rewriteConf = project.getConfigurations().maybeCreate("rewrite");
rewriteConf.setCanBeConsumed(false);

Provider<Set<File>> resolvedDependenciesProvider = project.provider(() -> getResolvedDependencies(project, extension, rewriteConf));
Callable<Configuration> detachedConf = new Callable<Configuration>() {
private Configuration cached;

@Override
public Configuration call() {
if (cached == null) {
cached = createDetachedConfiguration(project, extension, rewriteConf);
}
return cached;
}
};

TaskProvider<RewriteRunTask> rewriteRun = project.getTasks().register("rewriteRun", RewriteRunTask.class, task -> {
task.setExtension(extension);
task.setResolvedDependencies(resolvedDependenciesProvider);
task.dependsOn(rewriteConf);
task.getResolvedDependencies().from(detachedConf);
});

TaskProvider<RewriteDryRunTask> rewriteDryRun = project.getTasks().register("rewriteDryRun", RewriteDryRunTask.class, task -> {
task.setExtension(extension);
task.setResolvedDependencies(resolvedDependenciesProvider);
task.dependsOn(rewriteConf);
task.getResolvedDependencies().from(detachedConf);
});

TaskProvider<RewriteDiscoverTask> rewriteDiscover = project.getTasks().register("rewriteDiscover", RewriteDiscoverTask.class, task -> {
task.setExtension(extension);
task.setResolvedDependencies(resolvedDependenciesProvider);
task.dependsOn(rewriteConf);
task.getResolvedDependencies().from(detachedConf);
});

if (isRootProject) {
Expand Down Expand Up @@ -137,8 +140,12 @@ private static void configureProject(Project project, RewriteExtension extension
sourceSets.all(sourceSet -> {
// This is intended to ensure that any Groovy/Kotlin/etc. and dependent project sources are available
TaskProvider<Task> compileTask = project.getTasks().named(sourceSet.getCompileJavaTaskName());
rewriteRun.configure(task -> task.dependsOn(compileTask));
rewriteDryRun.configure(task -> task.dependsOn(compileTask));
for (TaskProvider<? extends AbstractRewriteTask> taskProvider : Arrays.asList(rewriteRun, rewriteDryRun)) {
taskProvider.configure(task -> {
task.dependsOn(compileTask);
task.getProjectClasspath().from(sourceSet.getRuntimeClasspath());
});
}
});

// Detect SourceSets which overlap other sourceSets and disable the compilation task of the overlapping
Expand All @@ -164,40 +171,37 @@ private static void configureProject(Project project, RewriteExtension extension
});
}

private Set<File> getResolvedDependencies(Project project, RewriteExtension extension, Configuration rewriteConf) {
if (resolvedDependencies == null) {
// Avoid Stream.concat here pending https://github.com/gradle/gradle/issues/33152
List<Dependency> dependencies = new ArrayList<>();
dependencies.addAll(knownRewriteDependencies(extension, project.getDependencies()));
dependencies.addAll(rewriteConf.getDependencies());
// 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.toArray(new Dependency[0]));

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
}
private static Configuration createDetachedConfiguration(Project project, RewriteExtension extension, Configuration rewriteConf) {
// Avoid Stream.concat here pending https://github.com/gradle/gradle/issues/33152
List<Dependency> dependencies = new ArrayList<>();
dependencies.addAll(knownRewriteDependencies(extension, project.getDependencies()));
dependencies.addAll(rewriteConf.getDependencies());
// 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.toArray(new Dependency[0]));

resolvedDependencies = detachedConf.resolve();
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
}
return resolvedDependencies;

return detachedConf;
}

private static Collection<Dependency> knownRewriteDependencies(RewriteExtension extension, DependencyHandler deps) {
Expand Down
Loading