diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index e35da34bfc..cf1ab40e24 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Fixed +* Make `SpotlessDiagnoseTask` compatible with the configuration cache` ([#2504](https://github.com/diffplug/spotless/pull/2504)) ## [7.0.4] - 2025-05-27 ### Fixed diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatterSpec.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatterSpec.java new file mode 100644 index 0000000000..ec945c6ca4 --- /dev/null +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatterSpec.java @@ -0,0 +1,68 @@ +/* + * Copyright 2025 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.diffplug.gradle.spotless; + +import java.nio.charset.Charset; + +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; + +import com.diffplug.spotless.ConfigurationCacheHackList; +import com.diffplug.spotless.Formatter; +import com.diffplug.spotless.LineEnding; + +final class FormatterSpec { + + private final Property encoding; + + @Input + Property getEncoding() { + return encoding; + } + + private final Property lineEndingsPolicy; + + @Input + Property getLineEndingsPolicy() { + return lineEndingsPolicy; + } + + private final ConfigurationCacheHackList stepsInternalRoundtrip = ConfigurationCacheHackList.forRoundtrip(); + + @Internal + public ConfigurationCacheHackList getStepsInternalRoundtrip() { + return stepsInternalRoundtrip; + } + + FormatterSpec(ObjectFactory objects) { + encoding = objects.property(String.class); + lineEndingsPolicy = objects.property(LineEnding.Policy.class); + + // set by SpotlessExtension, but possibly overridden by FormatExtension + getEncoding().convention("UTF-8"); + } + + Formatter buildFormatter() { + return Formatter.builder() + .lineEndingsPolicy(getLineEndingsPolicy().get()) + .encoding(Charset.forName(getEncoding().get())) + .steps(stepsInternalRoundtrip.getSteps()) + .build(); + } + +} diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java index 8b7253fd8c..f9d4e0b90b 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,7 +63,7 @@ static void performHook(SpotlessTaskImpl spotlessTask, IdeHook.State state) { } if (spotlessTask.getTarget().contains(file)) { GitRatchetGradle ratchet = spotlessTask.getRatchet(); - try (Formatter formatter = spotlessTask.buildFormatter()) { + try (Formatter formatter = spotlessTask.getFormatterSpec().buildFormatter()) { if (ratchet != null) { if (ratchet.isClean(spotlessTask.getProjectDir().get().getAsFile(), spotlessTask.getRootTreeSha(), file)) { dumpIsClean(); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessDiagnoseTask.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessDiagnoseTask.java index 68f6e63e75..f3e2200871 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessDiagnoseTask.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessDiagnoseTask.java @@ -22,8 +22,17 @@ import java.nio.file.Paths; import java.util.Locale; +import javax.inject.Inject; + import org.gradle.api.DefaultTask; -import org.gradle.api.tasks.Internal; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.FileSystemOperations; +import org.gradle.api.file.ProjectLayout; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.UntrackedTask; @@ -33,23 +42,39 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @UntrackedTask(because = "undeclared inputs/outputs") -public class SpotlessDiagnoseTask extends DefaultTask { - SpotlessTask source; +public abstract class SpotlessDiagnoseTask extends DefaultTask { + + @Inject + protected abstract ProjectLayout getProjectLayout(); + + @Inject + protected abstract FileSystemOperations getFileSystemOperations(); + + @Input + protected abstract Property getFormatName(); + + @Input + protected abstract Property getFormatter(); + + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + protected abstract ConfigurableFileCollection getTarget(); - @Internal - public SpotlessTask getSource() { - return source; + void init(SpotlessTask source) { + getFormatName().convention(source.formatName()); + getFormatter().convention(source.getFormatterSpec()); + getTarget().setFrom(source.getTarget()); } @TaskAction @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") public void performAction() throws IOException { - Path srcRoot = getProject().getProjectDir().toPath(); - Path diagnoseRoot = getProject().getLayout().getBuildDirectory().getAsFile().get() - .toPath().resolve("spotless-diagnose-" + source.formatName()); - getProject().delete(diagnoseRoot.toFile()); - try (Formatter formatter = source.buildFormatter()) { - for (File file : source.target) { + Path srcRoot = getProjectLayout().getProjectDirectory().getAsFile().toPath(); + Path diagnoseRoot = getProjectLayout().getBuildDirectory().getAsFile().get() + .toPath().resolve("spotless-diagnose-" + getFormatName().get()); + getFileSystemOperations().delete(spec -> spec.delete(diagnoseRoot)); + try (Formatter formatter = getFormatter().get().buildFormatter()) { + for (File file : getTarget()) { getLogger().debug("Running padded cell check on " + file); PaddedCell padded = PaddedCell.check(formatter, file); if (!padded.misbehaved()) { diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java index 75168f690a..7f593444f1 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,7 +96,9 @@ protected void createFormatTasks(String name, FormatExtension formatExtension) { // create the diagnose task TaskProvider diagnoseTask = tasks.register(taskName + DIAGNOSE, SpotlessDiagnoseTask.class, task -> { - task.source = spotlessTask.get(); + SpotlessTaskImpl source = spotlessTask.get(); + task.init(source); + task.setGroup(TASK_GROUP); task.mustRunAfter(BasePlugin.CLEAN_TASK_NAME); }); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java index 713c9a2e86..b12e5890b3 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java @@ -16,7 +16,6 @@ package com.diffplug.gradle.spotless; import java.io.File; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -24,6 +23,7 @@ import org.eclipse.jgit.lib.ObjectId; import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.FileCollection; import org.gradle.api.provider.Property; @@ -31,14 +31,15 @@ import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.PathSensitive; import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.work.DisableCachingByDefault; import org.gradle.work.Incremental; import com.diffplug.spotless.ConfigurationCacheHackList; -import com.diffplug.spotless.Formatter; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.LineEnding; import com.diffplug.spotless.LintSuppression; @@ -49,27 +50,29 @@ public abstract class SpotlessTask extends DefaultTask { @Internal abstract Property getTaskService(); - // set by SpotlessExtension, but possibly overridden by FormatExtension - protected String encoding = "UTF-8"; + private final FormatterSpec formatterSpec = new FormatterSpec(getProject().getObjects()); - @Input + @Nested + protected FormatterSpec getFormatterSpec() { + return formatterSpec; + } + + @Internal public String getEncoding() { - return encoding; + return getFormatterSpec().getEncoding().get(); } public void setEncoding(String encoding) { - this.encoding = Objects.requireNonNull(encoding); + getFormatterSpec().getEncoding().set(Objects.requireNonNull(encoding)); } - protected Provider lineEndingsPolicy = null; - - @Input + @Internal public Provider getLineEndingsPolicy() { - return lineEndingsPolicy; + return getFormatterSpec().getLineEndingsPolicy(); } public void setLineEndingsPolicy(Provider lineEndingsPolicy) { - this.lineEndingsPolicy = lineEndingsPolicy; + getFormatterSpec().getLineEndingsPolicy().set(lineEndingsPolicy); } /** @@ -135,21 +138,19 @@ public List getLintSuppressions() { return lintSuppressions; } - protected FileCollection target; + @Internal + protected abstract ConfigurableFileCollection getTargetInternal(); @PathSensitive(PathSensitivity.RELATIVE) @Incremental @InputFiles + @SkipWhenEmpty public FileCollection getTarget() { - return target; + return getTargetInternal(); } public void setTarget(Iterable target) { - if (target instanceof FileCollection) { - this.target = (FileCollection) target; - } else { - this.target = getProject().files(target); - } + getTargetInternal().setFrom(target); } protected File cleanDirectory = new File(getProject().getLayout().getBuildDirectory().getAsFile().get(), @@ -168,14 +169,8 @@ public File getLintsDirectory() { return lintsDirectory; } - private final ConfigurationCacheHackList stepsInternalRoundtrip = ConfigurationCacheHackList.forRoundtrip(); private final ConfigurationCacheHackList stepsInternalEquality = ConfigurationCacheHackList.forEquality(); - @Internal - public ConfigurationCacheHackList getStepsInternalRoundtrip() { - return stepsInternalRoundtrip; - } - @Input public ConfigurationCacheHackList getStepsInternalEquality() { return stepsInternalEquality; @@ -183,9 +178,9 @@ public ConfigurationCacheHackList getStepsInternalEquality() { public void setSteps(List steps) { PluginGradlePreconditions.requireElementsNonNull(steps); - this.stepsInternalRoundtrip.clear(); + this.formatterSpec.getStepsInternalRoundtrip().clear(); this.stepsInternalEquality.clear(); - this.stepsInternalRoundtrip.addAll(steps); + this.formatterSpec.getStepsInternalRoundtrip().addAll(steps); this.stepsInternalEquality.addAll(steps); } @@ -198,12 +193,4 @@ String formatName() { return name; } } - - Formatter buildFormatter() { - return Formatter.builder() - .lineEndingsPolicy(getLineEndingsPolicy().get()) - .encoding(Charset.forName(encoding)) - .steps(stepsInternalRoundtrip.getSteps()) - .build(); - } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTaskImpl.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTaskImpl.java index 7241f018d0..559d953b71 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTaskImpl.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTaskImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,7 +84,7 @@ public void performAction(InputChanges inputs) throws Exception { SpotlessTaskService taskService = getTaskService().get(); taskService.registerSourceAlreadyRan(this); - if (target == null) { + if (getTargetInternal().getFrom().isEmpty()) { throw new GradleException("You must specify 'Iterable target'"); } @@ -96,9 +96,9 @@ public void performAction(InputChanges inputs) throws Exception { Files.createDirectories(lintsDirectory.toPath()); } - try (Formatter formatter = buildFormatter()) { + try (Formatter formatter = getFormatterSpec().buildFormatter()) { GitRatchetGradle ratchet = getRatchet(); - for (FileChange fileChange : inputs.getFileChanges(target)) { + for (FileChange fileChange : inputs.getFileChanges(getTarget())) { File input = fileChange.getFile(); File projectDir = getProjectDir().get().getAsFile(); String relativePath = FormatExtension.relativize(projectDir, input); diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigurationCacheTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigurationCacheTest.java index e76585fe54..b4c114ab02 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigurationCacheTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigurationCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 DiffPlug + * Copyright 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,10 +84,12 @@ public void multipleRuns() throws IOException { gradleRunner().withArguments("spotlessApply", "--stacktrace").build(); gradleRunner().withArguments("spotlessApply").build(); gradleRunner().withArguments("spotlessApply").build(); + gradleRunner().withArguments("spotlessDiagnose").build(); setFile("test.java").toResource("java/googlejavaformat/JavaCodeUnformatted.test"); gradleRunner().withArguments("spotlessCheck").buildAndFail(); gradleRunner().withArguments("spotlessApply").build(); + gradleRunner().withArguments("spotlessDiagnose").build(); assertFile("test.java").sameAsResource("java/googlejavaformat/JavaCodeFormatted.test"); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java index 888f11f294..7359fddf29 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,7 +96,7 @@ String checkFailureMsg() { void diagnose() throws IOException { SpotlessDiagnoseTask diagnose = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Diagnose", SpotlessDiagnoseTask.class); - diagnose.source = source; + diagnose.init(source); diagnose.performAction(); }