Skip to content

Commit 166ab79

Browse files
committed
Migrate from our one-off to the design we'd like Gradle to adopt.
1 parent 3307b82 commit 166ab79

File tree

4 files changed

+125
-93
lines changed

4 files changed

+125
-93
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2021 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.gradle.spotless;
17+
18+
import java.io.File;
19+
import java.io.Serializable;
20+
import java.util.Collections;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
import java.util.Objects;
24+
25+
import javax.annotation.Nullable;
26+
27+
import org.gradle.api.GradleException;
28+
import org.gradle.api.Task;
29+
30+
import com.diffplug.spotless.FileSignature;
31+
32+
class JvmLocalCache {
33+
private static GradleException cacheIsStale() {
34+
return new GradleException("Spotless JVM-local cache is stale. Regenerate the cache with\n" +
35+
" " + (FileSignature.machineIsWin() ? "rmdir /q /s" : "rm -rf") + " .gradle/configuration-cache\n" +
36+
"To make this workaround obsolete, please upvote https://github.com/diffplug/spotless/issues/987");
37+
}
38+
39+
interface CacheKey<T> {
40+
T get();
41+
42+
void set(T value);
43+
}
44+
45+
static <T> CacheKey<T> createCacheKey(Task task, String keyName, @Nullable T initialValue) {
46+
CacheKeyImpl key = new CacheKeyImpl<T>(new InternalCacheKey(task.getProject().getProjectDir(), task.getPath(), keyName));
47+
if (initialValue != null) {
48+
key.set(initialValue);
49+
}
50+
return key;
51+
}
52+
53+
static class CacheKeyImpl<T> implements CacheKey<T>, Serializable {
54+
InternalCacheKey internalKey;
55+
56+
CacheKeyImpl(InternalCacheKey internalKey) {
57+
this.internalKey = internalKey;
58+
}
59+
60+
@Override
61+
public void set(T value) {
62+
daemonState.put(internalKey, value);
63+
}
64+
65+
@Override
66+
public T get() {
67+
Object value = daemonState.get(internalKey);
68+
if (value == null) {
69+
// TODO: throw TriggerConfigurationException(); (see https://github.com/diffplug/spotless/issues/987)
70+
throw cacheIsStale();
71+
} else {
72+
return (T) value;
73+
}
74+
}
75+
}
76+
77+
private static Map<InternalCacheKey, Object> daemonState = Collections.synchronizedMap(new HashMap<>());
78+
79+
private static class InternalCacheKey implements Serializable {
80+
private File projectDir;
81+
private String taskPath;
82+
private String keyName;
83+
84+
InternalCacheKey(File projectDir, String taskPath, String keyName) {
85+
this.projectDir = projectDir;
86+
this.taskPath = taskPath;
87+
this.keyName = keyName;
88+
}
89+
90+
@Override
91+
public boolean equals(Object o) {
92+
if (this == o)
93+
return true;
94+
if (o == null || getClass() != o.getClass())
95+
return false;
96+
InternalCacheKey that = (InternalCacheKey) o;
97+
return projectDir.equals(that.projectDir) && taskPath.equals(that.taskPath) && keyName.equals(that.keyName);
98+
}
99+
100+
@Override
101+
public int hashCode() {
102+
return Objects.hash(projectDir, taskPath, keyName);
103+
}
104+
}
105+
}

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

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.gradle.api.tasks.PathSensitivity;
3939
import org.gradle.work.Incremental;
4040

41+
import com.diffplug.gradle.spotless.JvmLocalCache.CacheKey;
4142
import com.diffplug.spotless.FormatExceptionPolicy;
4243
import com.diffplug.spotless.FormatExceptionPolicyStrict;
4344
import com.diffplug.spotless.Formatter;
@@ -48,10 +49,12 @@ public abstract class SpotlessTask extends DefaultTask {
4849
@Internal
4950
abstract Property<SpotlessTaskService> getTaskService();
5051

51-
private void hydrateIfNull(Object o) {
52-
if (o == null) {
53-
getTaskService().get().hydrate(this);
54-
}
52+
protected <T> CacheKey<T> createCacheKey(String keyName) {
53+
return createCacheKey(keyName, null);
54+
}
55+
56+
protected <T> CacheKey<T> createCacheKey(String keyName, @Nullable T initialValue) {
57+
return JvmLocalCache.createCacheKey(this, keyName, initialValue);
5558
}
5659

5760
// set by SpotlessExtension, but possibly overridden by FormatExtension
@@ -66,16 +69,15 @@ public void setEncoding(String encoding) {
6669
this.encoding = Objects.requireNonNull(encoding);
6770
}
6871

69-
protected transient LineEnding.Policy lineEndingsPolicy;
72+
protected final CacheKey<LineEnding.Policy> lineEndingsPolicy = createCacheKey("lineEndingsPolicy");
7073

7174
@Input
7275
public LineEnding.Policy getLineEndingsPolicy() {
73-
hydrateIfNull(lineEndingsPolicy);
74-
return lineEndingsPolicy;
76+
return lineEndingsPolicy.get();
7577
}
7678

7779
public void setLineEndingsPolicy(LineEnding.Policy lineEndingsPolicy) {
78-
this.lineEndingsPolicy = Objects.requireNonNull(lineEndingsPolicy);
80+
this.lineEndingsPolicy.set(lineEndingsPolicy);
7981
}
8082

8183
/*** API which performs git up-to-date tasks. */
@@ -90,7 +92,7 @@ public void setLineEndingsPolicy(LineEnding.Policy lineEndingsPolicy) {
9092
*/
9193
private transient ObjectId subtreeSha = ObjectId.zeroId();
9294
/** Stored so that the configuration cache can recreate the GitRatchetGradle state. */
93-
protected transient String ratchetFrom;
95+
protected String ratchetFrom;
9496

9597
public void setupRatchet(String ratchetFrom) {
9698
this.ratchetFrom = ratchetFrom;
@@ -119,7 +121,9 @@ ObjectId getRootTreeSha() {
119121

120122
@Input
121123
public ObjectId getRatchetSha() {
122-
hydrateIfNull(subtreeSha);
124+
if (subtreeSha == null) {
125+
setupRatchet(ratchetFrom);
126+
}
123127
return subtreeSha;
124128
}
125129

@@ -158,20 +162,19 @@ public File getOutputDirectory() {
158162
return outputDirectory;
159163
}
160164

161-
protected transient List<FormatterStep> steps = new ArrayList<>();
165+
protected final CacheKey<List<FormatterStep>> steps = createCacheKey("steps", new ArrayList<FormatterStep>());
162166

163167
@Input
164168
public List<FormatterStep> getSteps() {
165-
hydrateIfNull(steps);
166-
return Collections.unmodifiableList(steps);
169+
return Collections.unmodifiableList(steps.get());
167170
}
168171

169172
public void setSteps(List<FormatterStep> steps) {
170-
this.steps = PluginGradlePreconditions.requireElementsNonNull(steps);
173+
this.steps.set(PluginGradlePreconditions.requireElementsNonNull(steps));
171174
}
172175

173176
public boolean addStep(FormatterStep step) {
174-
return this.steps.add(Objects.requireNonNull(step));
177+
return this.steps.get().add(Objects.requireNonNull(step));
175178
}
176179

177180
/** Returns the name of this format. */
@@ -186,10 +189,10 @@ String formatName() {
186189

187190
Formatter buildFormatter() {
188191
return Formatter.builder()
189-
.lineEndingsPolicy(lineEndingsPolicy)
192+
.lineEndingsPolicy(lineEndingsPolicy.get())
190193
.encoding(Charset.forName(encoding))
191194
.rootDir(getProjectDir().get().getAsFile().toPath())
192-
.steps(steps)
195+
.steps(steps.get())
193196
.exceptionPolicy(exceptionPolicy)
194197
.build();
195198
}

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

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import java.nio.file.Files;
2121
import java.nio.file.Path;
2222
import java.nio.file.StandardCopyOption;
23-
import java.util.List;
24-
import java.util.Objects;
2523

2624
import javax.inject.Inject;
2725

@@ -37,10 +35,7 @@
3735
import org.gradle.work.InputChanges;
3836

3937
import com.diffplug.common.base.StringPrinter;
40-
import com.diffplug.spotless.FileSignature;
4138
import com.diffplug.spotless.Formatter;
42-
import com.diffplug.spotless.FormatterStep;
43-
import com.diffplug.spotless.LineEnding;
4439
import com.diffplug.spotless.PaddedCell;
4540

4641
@CacheableTask
@@ -69,7 +64,6 @@ public void performAction(InputChanges inputs) throws Exception {
6964
Files.createDirectories(outputDirectory.toPath());
7065
}
7166

72-
assertHydrated(this);
7367
try (Formatter formatter = buildFormatter()) {
7468
for (FileChange fileChange : inputs.getFileChanges(target)) {
7569
File input = fileChange.getFile();
@@ -131,42 +125,4 @@ private File getOutputFile(File input) {
131125
}
132126
return new File(outputDirectory, outputFileName);
133127
}
134-
135-
static boolean isHydrated(SpotlessTask task) {
136-
return task.lineEndingsPolicy != null;
137-
}
138-
139-
static void assertHydrated(SpotlessTask task) {
140-
if (!isHydrated(task)) {
141-
throw new GradleException("Spotless needs a workaround to support configuration cache:\n" +
142-
" (in your root build.gradle)\n" +
143-
" apply plugin: 'com.diffplug.spotless-setup\n" +
144-
" spotlessSetup { jvmLocalCache = true }\n" +
145-
"To make this workaround obsolete, please upvote https://github.com/diffplug/spotless/issues/987");
146-
}
147-
}
148-
149-
static GradleException cacheIsStale() {
150-
return new GradleException("Spotless JVM-local cache is stale. Regenerate the cache with\n" +
151-
" " + (FileSignature.machineIsWin() ? "rmdir /q /s" : "rm -rf") + " .gradle/configuration-cache\n" +
152-
"To make this workaround obsolete, please upvote https://github.com/diffplug/spotless/issues/987");
153-
}
154-
155-
static class LiveCache {
156-
LineEnding.Policy lineEndingsPolicy;
157-
List<FormatterStep> steps;
158-
String ratchetFrom;
159-
160-
LiveCache(SpotlessTask task) {
161-
lineEndingsPolicy = Objects.requireNonNull(task.lineEndingsPolicy);
162-
steps = Objects.requireNonNull(task.steps);
163-
ratchetFrom = Objects.requireNonNull(task.ratchetFrom);
164-
}
165-
166-
void hydrate(SpotlessTask task) {
167-
task.lineEndingsPolicy = lineEndingsPolicy;
168-
task.steps = steps;
169-
task.setupRatchet(ratchetFrom);
170-
}
171-
}
172128
}

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

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,46 +44,14 @@
4444
* apply already did).
4545
*/
4646
public abstract class SpotlessTaskService implements BuildService<BuildServiceParameters.None>, AutoCloseable, OperationCompletionListener {
47-
private boolean enableConfigCacheDaemonLocal;
4847
private Map<String, SpotlessApply> apply = Collections.synchronizedMap(new HashMap<>());
4948
private Map<String, SpotlessTask> source = Collections.synchronizedMap(new HashMap<>());
5049
private Map<String, Provisioner> provisioner = Collections.synchronizedMap(new HashMap<>());
5150

52-
void registerDependenciesTask(RegisterDependenciesTask task) {
53-
enableConfigCacheDaemonLocal = task.getJvmLocalCache();
54-
}
55-
5651
void registerSourceAlreadyRan(SpotlessTask task) {
5752
source.put(task.getPath(), task);
58-
if (enableConfigCacheDaemonLocal) {
59-
storeOrHydrate(task);
60-
}
61-
}
62-
63-
void hydrate(SpotlessTask task) {
64-
storeOrHydrate(task);
65-
}
66-
67-
private void storeOrHydrate(SpotlessTask task) {
68-
if (!enableConfigCacheDaemonLocal) {
69-
SpotlessTaskImpl.assertHydrated(task);
70-
return;
71-
}
72-
String cacheKey = task.getProjectDir().getAsFile().get().getAbsolutePath() + ">" + task.getPath();
73-
if (SpotlessTaskImpl.isHydrated(task)) {
74-
daemonLocalMap.put(cacheKey, new SpotlessTaskImpl.LiveCache(task));
75-
} else {
76-
SpotlessTaskImpl.LiveCache cached = daemonLocalMap.get(cacheKey);
77-
if (cached == null) {
78-
throw SpotlessTaskImpl.cacheIsStale();
79-
} else {
80-
cached.hydrate(task);
81-
}
82-
}
8353
}
8454

85-
private static final Map<String, SpotlessTaskImpl.LiveCache> daemonLocalMap = Collections.synchronizedMap(new HashMap<>());
86-
8755
Provisioner provisionerFor(Project project) {
8856
return provisioner.computeIfAbsent(project.getPath(), unused -> {
8957
return GradleProvisioner.newDedupingProvisioner(project);

0 commit comments

Comments
 (0)