Skip to content

Commit f52f7fd

Browse files
authored
SpotlessTaskService, towards configuration-cache (#982)
2 parents f86bb04 + eb8db28 commit f52f7fd

21 files changed

+373
-143
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This document is intended for Spotless developers.
1010
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
1111

1212
## [Unreleased]
13+
### Added
14+
* `DiffMessageFormatter` can now generate messages based on a folder of cleaned files, as an alternative to a `Formatter` ([#982](https://github.com/diffplug/spotless/pull/982)).
1315

1416
## [2.19.2] - 2021-10-26
1517
### Changed

gradle/spotless.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ spotless {
1616
// the rootProject doesn't have any java
1717
java {
1818
ratchetFrom 'origin/main'
19-
custom 'noInternalDeps', noInternalDepsClosure
2019
bumpThisNumberIfACustomStepChanges(1)
2120
licenseHeaderFile rootProject.file('gradle/spotless.license')
2221
importOrderFile rootProject.file('gradle/spotless.importorder')
2322
eclipse().configFile rootProject.file('gradle/spotless.eclipseformat.xml')
2423
trimTrailingWhitespace()
2524
removeUnusedImports()
25+
custom 'noInternalDeps', noInternalDepsClosure
2626
}
2727
}
2828
groovyGradle {

lib-extra/src/main/java/com/diffplug/spotless/extra/integration/DiffMessageFormatter.java

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 DiffPlug
2+
* Copyright 2016-2021 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,8 +18,10 @@
1818
import java.io.ByteArrayOutputStream;
1919
import java.io.File;
2020
import java.io.IOException;
21+
import java.nio.charset.Charset;
2122
import java.nio.charset.StandardCharsets;
2223
import java.nio.file.Files;
24+
import java.nio.file.Path;
2325
import java.util.List;
2426
import java.util.ListIterator;
2527
import java.util.Objects;
@@ -44,11 +46,74 @@ public static Builder builder() {
4446
return new Builder();
4547
}
4648

49+
interface CleanProvider {
50+
51+
Path getRootDir();
52+
53+
Charset getEncoding();
54+
55+
String getFormatted(File file, String rawUnix);
56+
}
57+
58+
private static class CleanProviderFormatter implements CleanProvider {
59+
private final Formatter formatter;
60+
61+
CleanProviderFormatter(Formatter formatter) {
62+
this.formatter = Objects.requireNonNull(formatter);
63+
}
64+
65+
@Override
66+
public Path getRootDir() {
67+
return formatter.getRootDir();
68+
}
69+
70+
@Override
71+
public Charset getEncoding() {
72+
return formatter.getEncoding();
73+
}
74+
75+
@Override
76+
public String getFormatted(File file, String rawUnix) {
77+
String unix = PaddedCell.check(formatter, file, rawUnix).canonical();
78+
return formatter.computeLineEndings(unix, file);
79+
}
80+
}
81+
82+
private static class CleanProviderFolder implements CleanProvider {
83+
private final Path rootDir;
84+
private final Path cleanDir;
85+
private final Charset encoding;
86+
87+
CleanProviderFolder(Path rootDir, Path cleanDir, String encoding) {
88+
this.rootDir = rootDir;
89+
this.cleanDir = cleanDir;
90+
this.encoding = Charset.forName(encoding);
91+
}
92+
93+
@Override
94+
public Path getRootDir() {
95+
return rootDir;
96+
}
97+
98+
@Override
99+
public Charset getEncoding() {
100+
return encoding;
101+
}
102+
103+
@Override
104+
public String getFormatted(File file, String rawUnix) {
105+
Path relative = rootDir.relativize(file.toPath());
106+
Path clean = cleanDir.resolve(rootDir.relativize(file.toPath()));
107+
byte[] content = Errors.rethrow().get(() -> Files.readAllBytes(clean));
108+
return new String(content, encoding);
109+
}
110+
}
111+
47112
public static class Builder {
48113
private Builder() {}
49114

50115
private String runToFix;
51-
private Formatter formatter;
116+
private CleanProvider formatter;
52117
private List<File> problemFiles;
53118

54119
/** "Run 'gradlew spotlessApply' to fix these violations." */
@@ -58,7 +123,12 @@ public Builder runToFix(String runToFix) {
58123
}
59124

60125
public Builder formatter(Formatter formatter) {
61-
this.formatter = Objects.requireNonNull(formatter);
126+
this.formatter = new CleanProviderFormatter(formatter);
127+
return this;
128+
}
129+
130+
public Builder formatterFolder(Path rootDir, Path cleanDir, String encoding) {
131+
this.formatter = new CleanProviderFolder(rootDir, cleanDir, encoding);
62132
return this;
63133
}
64134

@@ -164,12 +234,11 @@ private void addIntendedLine(String indent, String line) {
164234
private static String diff(Builder builder, File file) throws IOException {
165235
String raw = new String(Files.readAllBytes(file.toPath()), builder.formatter.getEncoding());
166236
String rawUnix = LineEnding.toUnix(raw);
167-
String formattedUnix;
168-
formattedUnix = PaddedCell.check(builder.formatter, file, rawUnix).canonical();
237+
String formatted = builder.formatter.getFormatted(file, rawUnix);
238+
String formattedUnix = LineEnding.toUnix(formatted);
169239

170240
if (rawUnix.equals(formattedUnix)) {
171241
// the formatting is fine, so it's a line-ending issue
172-
String formatted = builder.formatter.computeLineEndings(formattedUnix, file);
173242
return diffWhitespaceLineEndings(raw, formatted, false, true);
174243
} else {
175244
return diffWhitespaceLineEndings(rawUnix, formattedUnix, true, false);

plugin-gradle/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
55
## [Unreleased]
66
### Changed
77
* **BREAKING** Previously, many projects required `buildscript { repositories { mavenCentral() }}` at the top of their root project, because Spotless resolved its dependencies using the buildscript repositories. Spotless now resolves its dependencies from the normal project repositories of the root project, which means that you can remove the `buildscript {}` block, but you still need `repositories { mavenCentral() }` (or similar) in the root project.
8+
* **BREAKING** `createIndepentApplyTask(String taskName)` now requires that `taskName` does not end with `Apply`
89
* Bump minimum required Gradle from `6.1` to `6.1.1`.
910

1011
## [5.17.1] - 2021-10-26

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.gradle.api.file.FileCollection;
4141
import org.gradle.api.plugins.BasePlugin;
4242

43+
import com.diffplug.common.base.Preconditions;
4344
import com.diffplug.spotless.FormatExceptionPolicyStrict;
4445
import com.diffplug.spotless.FormatterFunc;
4546
import com.diffplug.spotless.FormatterStep;
@@ -754,19 +755,22 @@ protected Project getProject() {
754755
*
755756
* The returned task will not be hooked up to the global {@code spotlessApply}, and there will be no corresponding {@code check} task.
756757
*
758+
* The task name must not end with `Apply`.
759+
*
757760
* NOTE: does not respect the rarely-used <a href="https://github.com/diffplug/spotless/blob/b7f8c551a97dcb92cc4b0ee665448da5013b30a3/plugin-gradle/README.md#can-i-apply-spotless-to-specific-files">{@code spotlessFiles} property</a>.
758761
*/
759762
public SpotlessApply createIndependentApplyTask(String taskName) {
763+
Preconditions.checkArgument(!taskName.endsWith(SpotlessExtension.APPLY), "Task name must not end with " + SpotlessExtension.APPLY);
760764
// create and setup the task
761-
SpotlessTask spotlessTask = spotless.project.getTasks().create(taskName + "Helper", SpotlessTaskImpl.class);
765+
SpotlessTaskImpl spotlessTask = spotless.project.getTasks().create(taskName + SpotlessTaskService.INDEPENDENT_HELPER, SpotlessTaskImpl.class);
766+
spotlessTask.init(spotless.getTaskService());
762767
setupTask(spotlessTask);
763768
// enforce the clean ordering
764769
Task clean = spotless.project.getTasks().getByName(BasePlugin.CLEAN_TASK_NAME);
765770
spotlessTask.mustRunAfter(clean);
766771
// create the apply task
767772
SpotlessApply applyTask = spotless.project.getTasks().create(taskName, SpotlessApply.class);
768-
applyTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory());
769-
applyTask.linkSource(spotlessTask);
773+
applyTask.init(spotlessTask);
770774
applyTask.dependsOn(spotlessTask);
771775

772776
return applyTask;

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import javax.annotation.Nullable;
2121

22-
import org.gradle.api.Project;
2322
import org.gradle.api.services.BuildService;
2423
import org.gradle.api.services.BuildServiceParameters;
2524
import org.gradle.tooling.events.FinishEvent;
@@ -28,15 +27,15 @@
2827
import com.diffplug.spotless.extra.GitRatchet;
2928

3029
/** Gradle implementation of GitRatchet. */
31-
public abstract class GitRatchetGradle extends GitRatchet<Project> implements BuildService<BuildServiceParameters.None>, OperationCompletionListener {
30+
public abstract class GitRatchetGradle extends GitRatchet<File> implements BuildService<BuildServiceParameters.None>, OperationCompletionListener {
3231
@Override
33-
protected File getDir(Project project) {
34-
return project.getProjectDir();
32+
protected File getDir(File project) {
33+
return project;
3534
}
3635

3736
@Override
38-
protected @Nullable Project getParent(Project project) {
39-
return project.getParent();
37+
protected @Nullable File getParent(File project) {
38+
return project.getParentFile();
4039
}
4140

4241
@Override

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 DiffPlug
2+
* Copyright 2016-2021 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ private static void dumpIsClean() {
3333
System.err.println("IS CLEAN");
3434
}
3535

36-
static void performHook(SpotlessTask spotlessTask) {
36+
static void performHook(SpotlessTaskImpl spotlessTask) {
3737
String path = (String) spotlessTask.getProject().property(PROPERTY);
3838
File file = new File(path);
3939
if (!file.isAbsolute()) {
@@ -43,7 +43,7 @@ static void performHook(SpotlessTask spotlessTask) {
4343
if (spotlessTask.getTarget().contains(file)) {
4444
try (Formatter formatter = spotlessTask.buildFormatter()) {
4545
if (spotlessTask.ratchet != null) {
46-
if (spotlessTask.ratchet.isClean(spotlessTask.getProject(), spotlessTask.rootTreeSha, file)) {
46+
if (spotlessTask.ratchet.isClean(spotlessTask.getProjectDir().get().getAsFile(), spotlessTask.rootTreeSha, file)) {
4747
dumpIsClean();
4848
return;
4949
}

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SerializableMisc.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 DiffPlug
2+
* Copyright 2016-2021 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ static void toFile(Serializable obj, File file) {
3939
}
4040
}
4141

42-
static <T extends Serializable> T fromFile(Class<T> clazz, File file) {
42+
static <T> T fromFile(Class<T> clazz, File file) {
4343
try (InputStream input = Files.asByteSource(file).openBufferedStream()) {
4444
return fromStream(clazz, input);
4545
} catch (IOException e) {

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessApply.java

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,18 @@
2020
import java.nio.file.Files;
2121
import java.nio.file.StandardCopyOption;
2222

23-
import org.gradle.api.DefaultTask;
2423
import org.gradle.api.file.ConfigurableFileTree;
2524
import org.gradle.api.file.FileVisitDetails;
2625
import org.gradle.api.file.FileVisitor;
27-
import org.gradle.api.tasks.Internal;
2826
import org.gradle.api.tasks.TaskAction;
2927

30-
public class SpotlessApply extends DefaultTask {
31-
private SpotlessTask source;
32-
33-
/** Bidirectional link between Apply and Spotless allows check to know if Apply ran or not. */
34-
void linkSource(SpotlessTask source) {
35-
this.source = source;
36-
source.applyTask = this;
37-
}
38-
39-
private File spotlessOutDirectory;
40-
41-
@Internal
42-
public File getSpotlessOutDirectory() {
43-
return spotlessOutDirectory;
44-
}
45-
46-
public void setSpotlessOutDirectory(File spotlessOutDirectory) {
47-
this.spotlessOutDirectory = spotlessOutDirectory;
48-
}
49-
28+
public abstract class SpotlessApply extends SpotlessTaskService.ClientTask {
5029
@TaskAction
5130
public void performAction() {
52-
ConfigurableFileTree files = getProject().fileTree(spotlessOutDirectory);
31+
getTaskService().get().registerApplyAlreadyRan(this);
32+
ConfigurableFileTree files = getConfigCacheWorkaround().fileTree().from(getSpotlessOutDirectory().get());
5333
if (files.isEmpty()) {
54-
getState().setDidWork(source.getDidWork());
34+
getState().setDidWork(sourceDidWork());
5535
} else {
5636
files.visit(new FileVisitor() {
5737
@Override
@@ -62,7 +42,7 @@ public void visitDir(FileVisitDetails fileVisitDetails) {
6242
@Override
6343
public void visitFile(FileVisitDetails fileVisitDetails) {
6444
String path = fileVisitDetails.getPath();
65-
File originalSource = new File(getProject().getProjectDir(), path);
45+
File originalSource = new File(getProjectDir().get().getAsFile(), path);
6646
try {
6747
getLogger().debug("Copying " + fileVisitDetails.getFile() + " to " + originalSource);
6848
Files.copy(fileVisitDetails.getFile().toPath(), originalSource.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);

0 commit comments

Comments
 (0)