Skip to content

Commit a074d53

Browse files
authored
Allow setting the local P2 cache dir in the Spotless Gradle plugin (#2944)
2 parents e0d466e + a266fc2 commit a074d53

16 files changed

Lines changed: 316 additions & 13 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1111

1212
## [Unreleased]
1313
### Fixed
14+
- `P2Provisioner` now passes cache directory overrides directly to Solstice. ([#2944](https://github.com/diffplug/spotless/pull/2944))
1415
- `forbidWildcardImports` and `forbidModuleImports` now detect imports that have leading whitespace (indentation/tabs). ([#2939](https://github.com/diffplug/spotless/pull/2939))
1516
- `versionCatalog` step no longer splits long inline tables across multiple lines — Gradle's TOML 1.0 parser cannot read multi-line inline tables. The `maxLineLength` option has been removed. ([#2948](https://github.com/diffplug/spotless/issues/2948))
1617
### Changes

lib-extra/src/main/java/com/diffplug/spotless/extra/P2Provisioner.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public interface P2Provisioner {
4242
*
4343
* @param modelWrapper wrapper around P2Model describing repositories and plugins to install
4444
* @param mavenProvisioner provisioner for Maven dependencies (some P2 bundles are on Maven Central)
45-
* @param cacheDirectory optional cache directory override
45+
* @param cacheDirectory optional cache directory override passed directly to Solstice
4646
* @return ordered list of JAR files forming the classpath
4747
*/
4848
List<File> provisionP2Dependencies(
@@ -55,7 +55,7 @@ static P2Provisioner createDefault() {
5555
return (modelWrapper, mavenProvisioner, cacheDirectory) -> {
5656
try {
5757
if (cacheDirectory != null) {
58-
CacheLocations.override_p2data = cacheDirectory.toPath().resolve("dev/equo/p2-data").toFile();
58+
CacheLocations.override_p2data = cacheDirectory;
5959
}
6060
P2Model model = modelWrapper.unwrap();
6161
P2QueryResult query = model.query(P2ClientCache.PREFER_OFFLINE, P2QueryCache.ALLOW);

lib/src/main/java/com/diffplug/spotless/Formatter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2025 DiffPlug
2+
* Copyright 2016-2026 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.

plugin-gradle/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).
44

55
## [Unreleased]
6+
### Added
7+
- Add `cacheDirectory(...)` to `eclipse()`, `eclipseCdt()`, and `greclipse()`; the default P2 cache is `$GRADLE_USER_HOME/caches/p2-data`. ([#2944](https://github.com/diffplug/spotless/pull/2944))
68
### Fixed
79
- `forbidWildcardImports` and `forbidModuleImports` now detect imports that have leading whitespace (indentation/tabs). ([#2939](https://github.com/diffplug/spotless/pull/2939))
810
- `versionCatalog()` no longer splits long inline tables across multiple lines — Gradle's TOML 1.0 parser cannot read multi-line inline tables. The `maxLineLength` option has been removed. ([#2948](https://github.com/diffplug/spotless/issues/2948))

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,19 @@ public GrEclipseConfig withP2Mirrors(Map<String, String> mirrors) {
9292
extension.replaceStep(builder.build());
9393
return this;
9494
}
95+
96+
/**
97+
* Overrides the directory used to cache the P2 dependencies fetched by
98+
* Equo/Solstice. Defaults to {@code $GRADLE_USER_HOME/caches/p2-data}.
99+
*
100+
* <p>Useful when the default location is not writable, or when you want to
101+
* place the cache elsewhere.
102+
*/
103+
public GrEclipseConfig cacheDirectory(Object cacheDirectory) {
104+
Objects.requireNonNull(cacheDirectory);
105+
builder.setCacheDirectory(extension.getProject().file(cacheDirectory));
106+
extension.replaceStep(builder.build());
107+
return this;
108+
}
95109
}
96110
}

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

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

2020
import java.util.List;
2121
import java.util.Map;
22+
import java.util.Objects;
2223

2324
import javax.inject.Inject;
2425

@@ -81,6 +82,20 @@ public EclipseConfig withP2Mirrors(Map<String, String> mirrors) {
8182
replaceStep(builder.build());
8283
return this;
8384
}
85+
86+
/**
87+
* Overrides the directory used to cache the P2 dependencies fetched by
88+
* Equo/Solstice. Defaults to {@code $GRADLE_USER_HOME/caches/p2-data}.
89+
*
90+
* <p>Useful when the default location is not writable, or when you want to
91+
* place the cache elsewhere.
92+
*/
93+
public EclipseConfig cacheDirectory(Object cacheDirectory) {
94+
Objects.requireNonNull(cacheDirectory);
95+
builder.setCacheDirectory(getProject().file(cacheDirectory));
96+
replaceStep(builder.build());
97+
return this;
98+
}
8499
}
85100

86101
@Override

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public DedupingProvisioner dedupingProvisioner(Project project) {
6161

6262
public DedupingP2Provisioner dedupingP2Provisioner(Project project) {
6363
return switch (this) {
64-
case ROOT_PROJECT, ROOT_BUILDSCRIPT -> new DedupingP2Provisioner(P2Provisioner.createDefault());
64+
case ROOT_PROJECT, ROOT_BUILDSCRIPT -> new DedupingP2Provisioner(P2Provisioner.createDefault(), defaultP2CacheDirectory(project));
6565
default -> throw Unhandled.enumException(this);
6666
};
6767
}
@@ -156,6 +156,10 @@ private static Provisioner forConfigurationContainer(Project project, Configurat
156156

157157
private static final Logger LOGGER = LoggerFactory.getLogger(GradleProvisioner.class);
158158

159+
static File defaultP2CacheDirectory(Project project) {
160+
return new File(project.getGradle().getGradleUserHomeDir(), "caches/p2-data");
161+
}
162+
159163
/** Models a request to the provisioner. */
160164
private static class Request {
161165
final boolean withTransitives;
@@ -199,9 +203,15 @@ public String toString() {
199203
static class DedupingP2Provisioner implements P2Provisioner {
200204
private final Map<P2Request, List<File>> cache = new HashMap<>();
201205
private final P2Provisioner p2Provisioner;
206+
@Nullable private final File defaultCacheDirectory;
202207

203208
public DedupingP2Provisioner(P2Provisioner p2Provisioner) {
209+
this(p2Provisioner, null);
210+
}
211+
212+
public DedupingP2Provisioner(P2Provisioner p2Provisioner, @Nullable File defaultCacheDirectory) {
204213
this.p2Provisioner = p2Provisioner;
214+
this.defaultCacheDirectory = defaultCacheDirectory;
205215
}
206216

207217
@Override
@@ -210,33 +220,35 @@ public synchronized List<File> provisionP2Dependencies(
210220
Provisioner mavenProvisioner,
211221
@Nullable File cacheDirectory) throws IOException {
212222

223+
File effectiveCacheDirectory = effectiveCacheDirectory(cacheDirectory);
213224
P2Request req = new P2Request(
214225
List.copyOf(modelWrapper.getP2Repos()),
215226
List.copyOf(modelWrapper.getInstallList()),
216227
Set.copyOf(modelWrapper.getFilterNames()),
217228
List.copyOf(modelWrapper.getPureMaven()),
218229
modelWrapper.isUseMavenCentral(),
219-
cacheDirectory);
230+
effectiveCacheDirectory);
220231

221232
List<File> result = cache.get(req);
222233
if (result != null) {
223234
return result;
224235
}
225236

226-
result = p2Provisioner.provisionP2Dependencies(modelWrapper, mavenProvisioner, cacheDirectory);
237+
result = p2Provisioner.provisionP2Dependencies(modelWrapper, mavenProvisioner, effectiveCacheDirectory);
227238
cache.put(req, List.copyOf(result));
228239
return result;
229240
}
230241

231242
/** A child P2Provisioner which retrieves cached elements only. */
232243
final P2Provisioner cachedOnly = (modelWrapper, mavenProvisioner, cacheDirectory) -> {
244+
File effectiveCacheDirectory = effectiveCacheDirectory(cacheDirectory);
233245
P2Request req = new P2Request(
234246
List.copyOf(modelWrapper.getP2Repos()),
235247
List.copyOf(modelWrapper.getInstallList()),
236248
Set.copyOf(modelWrapper.getFilterNames()),
237249
List.copyOf(modelWrapper.getPureMaven()),
238250
modelWrapper.isUseMavenCentral(),
239-
cacheDirectory);
251+
effectiveCacheDirectory);
240252
List<File> result;
241253
synchronized (cache) {
242254
result = cache.get(req);
@@ -247,6 +259,10 @@ public synchronized List<File> provisionP2Dependencies(
247259
throw new GradleException("P2 dependencies not predeclared. Add Eclipse formatter configuration to the `spotlessPredeclare` block in the root project.");
248260
};
249261

262+
@Nullable private File effectiveCacheDirectory(@Nullable File cacheDirectory) {
263+
return cacheDirectory != null ? cacheDirectory : defaultCacheDirectory;
264+
}
265+
250266
/**
251267
* Cache key capturing all P2Model state that affects query results.
252268
* Based on P2Model fields from equo-ide:

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,20 @@ public EclipseConfig withP2Mirrors(Map<String, String> mirrors) {
385385
return this;
386386
}
387387

388+
/**
389+
* Overrides the directory used to cache the P2 dependencies fetched by
390+
* Equo/Solstice. Defaults to {@code $GRADLE_USER_HOME/caches/p2-data}.
391+
*
392+
* <p>Useful when the default location is not writable, or when you want to
393+
* place the cache elsewhere.
394+
*/
395+
public EclipseConfig cacheDirectory(Object cacheDirectory) {
396+
Objects.requireNonNull(cacheDirectory);
397+
builder.setCacheDirectory(getProject().file(cacheDirectory));
398+
replaceStep(builder.build());
399+
return this;
400+
}
401+
388402
}
389403

390404
/** Removes newlines between type annotations and types. */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ P2Provisioner p2ProvisionerFor(SpotlessExtension spotless) {
8585
return predeclaredP2Provisioner.cachedOnly;
8686
} else {
8787
return p2Provisioner.computeIfAbsent(spotless.project.getPath(),
88-
unused -> new GradleProvisioner.DedupingP2Provisioner(P2Provisioner.createDefault()));
88+
unused -> new GradleProvisioner.DedupingP2Provisioner(P2Provisioner.createDefault(), GradleProvisioner.defaultP2CacheDirectory(spotless.project)));
8989
}
9090
}
9191
}

plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleProvisionerTest.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424
import java.util.Set;
2525
import java.util.concurrent.atomic.AtomicInteger;
26+
import java.util.concurrent.atomic.AtomicReference;
2627
import java.util.function.Function;
2728
import java.util.stream.Stream;
2829

@@ -262,6 +263,72 @@ void cachedOnlyCacheHitReturnsResult() throws IOException {
262263
assertThat(result).isNotEmpty();
263264
}
264265

266+
@Test
267+
void defaultCacheDirectoryUsedWhenNoOverride() throws IOException {
268+
AtomicReference<File> capturedCacheDirectory = new AtomicReference<>();
269+
File defaultCacheDirectory = new File("gradle-home/caches/p2-data");
270+
P2Provisioner underlying = (modelWrapper, mavenProvisioner, cacheDirectory) -> {
271+
capturedCacheDirectory.set(cacheDirectory);
272+
return List.of(new File("/mock/p2.jar"));
273+
};
274+
GradleProvisioner.DedupingP2Provisioner deduping = new GradleProvisioner.DedupingP2Provisioner(underlying, defaultCacheDirectory);
275+
276+
P2ModelWrapper model = createMockModel(
277+
List.of("https://download.eclipse.org/eclipse/updates/4.26/"),
278+
List.of("org.eclipse.jdt.core"),
279+
Set.of(),
280+
List.of(),
281+
true,
282+
null);
283+
284+
deduping.provisionP2Dependencies(model, mockProvisioner(), null);
285+
286+
assertThat(capturedCacheDirectory.get()).isEqualTo(defaultCacheDirectory);
287+
}
288+
289+
@Test
290+
void explicitCacheDirectoryOverridesDefault() throws IOException {
291+
AtomicReference<File> capturedCacheDirectory = new AtomicReference<>();
292+
File defaultCacheDirectory = new File("gradle-home/caches/p2-data");
293+
File explicitCacheDirectory = new File("project/.spotless-p2");
294+
P2Provisioner underlying = (modelWrapper, mavenProvisioner, cacheDirectory) -> {
295+
capturedCacheDirectory.set(cacheDirectory);
296+
return List.of(new File("/mock/p2.jar"));
297+
};
298+
GradleProvisioner.DedupingP2Provisioner deduping = new GradleProvisioner.DedupingP2Provisioner(underlying, defaultCacheDirectory);
299+
300+
P2ModelWrapper model = createMockModel(
301+
List.of("https://download.eclipse.org/eclipse/updates/4.26/"),
302+
List.of("org.eclipse.jdt.core"),
303+
Set.of(),
304+
List.of(),
305+
true,
306+
null);
307+
308+
deduping.provisionP2Dependencies(model, mockProvisioner(), explicitCacheDirectory);
309+
310+
assertThat(capturedCacheDirectory.get()).isEqualTo(explicitCacheDirectory);
311+
}
312+
313+
@Test
314+
void cachedOnlyUsesDefaultCacheDirectoryForLookup() throws IOException {
315+
File defaultCacheDirectory = new File("gradle-home/caches/p2-data");
316+
P2Provisioner underlying = mockP2Provisioner(new AtomicInteger(0));
317+
GradleProvisioner.DedupingP2Provisioner deduping = new GradleProvisioner.DedupingP2Provisioner(underlying, defaultCacheDirectory);
318+
319+
P2ModelWrapper model = createMockModel(
320+
List.of("https://download.eclipse.org/eclipse/updates/4.26/"),
321+
List.of("org.eclipse.jdt.core"),
322+
Set.of(),
323+
List.of(),
324+
true,
325+
null);
326+
327+
deduping.provisionP2Dependencies(model, mockProvisioner(), null);
328+
329+
assertThat(deduping.cachedOnly.provisionP2Dependencies(model, mockProvisioner(), null)).isNotEmpty();
330+
}
331+
265332
@Test
266333
void cachedOnlyCacheMissThrowsException() {
267334
P2Provisioner underlying = mockP2Provisioner(new AtomicInteger(0));

0 commit comments

Comments
 (0)