Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This document is intended for Spotless developers.
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Added
- Add `P2Provisioner` interface in lib-extra to enable build-tool-specific caching strategies for Eclipse P2 dependencies, fixing OutOfMemoryError in large multi-project builds. ([#2788](https://github.com/diffplug/spotless/issues/2788))

## [4.2.0] - 2026-01-22

Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ VER_JUNIT=6.0.2
VER_ASSERTJ=3.27.6
VER_MOCKITO=5.21.0
VER_SELFIE=2.5.5
VER_SOLSTICE=1.8.1
3 changes: 2 additions & 1 deletion lib-extra/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ version = rootProject.spotlessChangelog.versionNext
apply from: rootProject.file('gradle/java-setup.gradle')
apply from: rootProject.file('gradle/java-publish.gradle')

String VER_SOLSTICE = '1.8.1'
dependencies {
api projects.lib
// misc useful utilities
Expand All @@ -25,6 +24,8 @@ dependencies {

// testing
testImplementation projects.testlib
testImplementation "com.diffplug.durian:durian-io:${VER_DURIAN}"
testImplementation "com.google.code.findbugs:jsr305:${VER_JSR_305}"
testImplementation "org.junit.jupiter:junit-jupiter:${VER_JUNIT}"
testImplementation "org.assertj:assertj-core:${VER_ASSERTJ}"
testImplementation "com.diffplug.durian:durian-testlib:${VER_DURIAN}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 DiffPlug
* Copyright 2016-2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,7 +18,6 @@
import static java.util.stream.Collectors.toMap;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -38,19 +37,15 @@
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.SerializedFunction;

import dev.equo.solstice.NestedJars;
import dev.equo.solstice.p2.CacheLocations;
import dev.equo.solstice.p2.P2ClientCache;
import dev.equo.solstice.p2.P2Model;
import dev.equo.solstice.p2.P2QueryCache;
import dev.equo.solstice.p2.P2QueryResult;

/**
* Generic Eclipse based formatter step {@link State} builder.
*/
public abstract class EquoBasedStepBuilder {
private final String formatterName;
private final Provisioner mavenProvisioner;
private final P2Provisioner p2Provisioner;
private final SerializedFunction<State, FormatterFunc> stateToFormatter;
private final ImmutableMap.Builder<String, String> stepProperties;
private String formatterVersion;
Expand All @@ -64,12 +59,14 @@ public abstract class EquoBasedStepBuilder {
protected EquoBasedStepBuilder(
String formatterName,
Provisioner mavenProvisioner,
P2Provisioner p2Provisioner,
@Nullable String defaultVersion,
SerializedFunction<State, FormatterFunc> stateToFormatter,
ImmutableMap.Builder<String, String> stepProperties) {

this.formatterName = formatterName;
this.mavenProvisioner = mavenProvisioner;
this.p2Provisioner = p2Provisioner;
this.formatterVersion = defaultVersion;
this.stateToFormatter = stateToFormatter;
this.stepProperties = stepProperties;
Expand Down Expand Up @@ -125,25 +122,9 @@ protected void addPlatformRepo(P2Model model, String version) {
/** Returns the FormatterStep (whose state will be calculated lazily). */
public FormatterStep build() {
var roundtrippableState = new EquoStep(formatterVersion, settingProperties, settingXml, FileSignature.promise(settingsFiles), JarState.promise(() -> {
P2QueryResult query;
try {
if (cacheDirectory != null) {
CacheLocations.override_p2data = cacheDirectory.toPath().resolve("dev/equo/p2-data").toFile();
}
query = createModelWithMirrors().query(P2ClientCache.PREFER_OFFLINE, P2QueryCache.ALLOW);
} catch (Exception x) {
throw new IOException("Failed to load " + formatterName + ": " + x, x);
}
var classpath = new ArrayList<File>();
var mavenDeps = new ArrayList<String>();
mavenDeps.add("dev.equo.ide:solstice:1.8.1");
mavenDeps.add("com.diffplug.durian:durian-swt.os:4.3.1");
mavenDeps.addAll(query.getJarsOnMavenCentral());
classpath.addAll(mavenProvisioner.provisionWithTransitives(false, mavenDeps));
classpath.addAll(query.getJarsNotOnMavenCentral());
for (var nested : NestedJars.inFiles(query.getJarsNotOnMavenCentral()).extractAllNestedJars()) {
classpath.add(nested.getValue());
}
P2Model model = createModelWithMirrors();
P2ModelWrapper modelWrapper = P2ModelWrapper.wrap(model);
List<File> classpath = p2Provisioner.provisionP2Dependencies(modelWrapper, mavenProvisioner, cacheDirectory);
return JarState.preserveOrder(classpath);
}), stepProperties.build());
return FormatterStep.create(formatterName, roundtrippableState, EquoStep::state, stateToFormatter);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2016-2026 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.spotless.extra;

import java.util.Collection;
import java.util.Set;

import dev.equo.solstice.p2.P2Model;

/**
* Wrapper for P2Model that exposes only the data needed for caching,
* without leaking the P2Model type to consumers.
*/
public final class P2ModelWrapper {
private final P2Model model;

private P2ModelWrapper(P2Model model) {
this.model = model;
}

public static P2ModelWrapper wrap(P2Model model) {
return new P2ModelWrapper(model);
}

public P2Model unwrap() {
return model;
}

public Collection<String> getP2Repos() {
return model.getP2repo();
}

public Collection<String> getInstallList() {
return model.getInstall();
}

public Set<String> getFilterNames() {
return model.getFilters().keySet();
}

public Collection<String> getPureMaven() {
return model.getPureMaven();
}

public boolean isUseMavenCentral() {
return model.useMavenCentral;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2016-2026 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.spotless.extra;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;

import com.diffplug.spotless.Provisioner;

import dev.equo.solstice.NestedJars;
import dev.equo.solstice.p2.CacheLocations;
import dev.equo.solstice.p2.P2ClientCache;
import dev.equo.solstice.p2.P2Model;
import dev.equo.solstice.p2.P2QueryCache;
import dev.equo.solstice.p2.P2QueryResult;

/**
* Provisions dependencies from Eclipse P2 repositories.
* Similar to {@link Provisioner} but for P2/OSGi bundles.
*/
@FunctionalInterface
public interface P2Provisioner {
/**
* Resolves P2 dependencies and returns the classpath.
*
* @param modelWrapper wrapper around P2Model describing repositories and plugins to install
* @param mavenProvisioner provisioner for Maven dependencies (some P2 bundles are on Maven Central)
* @param cacheDirectory optional cache directory override
* @return ordered list of JAR files forming the classpath
*/
List<File> provisionP2Dependencies(
P2ModelWrapper modelWrapper,
Provisioner mavenProvisioner,
@Nullable File cacheDirectory) throws IOException;

/** Creates a non-caching P2Provisioner for simple use cases. */
static P2Provisioner createDefault() {
return (modelWrapper, mavenProvisioner, cacheDirectory) -> {
try {
if (cacheDirectory != null) {
CacheLocations.override_p2data = cacheDirectory.toPath().resolve("dev/equo/p2-data").toFile();
}
P2Model model = modelWrapper.unwrap();
P2QueryResult query = model.query(P2ClientCache.PREFER_OFFLINE, P2QueryCache.ALLOW);
var classpath = new ArrayList<File>();
var mavenDeps = new ArrayList<String>();
mavenDeps.add("dev.equo.ide:solstice:1.8.1");
mavenDeps.add("com.diffplug.durian:durian-swt.os:4.3.1");
mavenDeps.addAll(query.getJarsOnMavenCentral());
classpath.addAll(mavenProvisioner.provisionWithTransitives(false, mavenDeps));
classpath.addAll(query.getJarsNotOnMavenCentral());
for (var nested : NestedJars.inFiles(query.getJarsNotOnMavenCentral()).extractAllNestedJars()) {
classpath.add(nested.getValue());
}
return classpath;
} catch (Exception e) {
throw new IOException("Failed to provision P2 dependencies", e);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 DiffPlug
* Copyright 2016-2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,6 +23,7 @@
import com.diffplug.spotless.Jvm;
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.extra.EquoBasedStepBuilder;
import com.diffplug.spotless.extra.P2Provisioner;

import dev.equo.solstice.p2.P2Model;

Expand All @@ -45,8 +46,8 @@ public static String defaultVersion() {
}

/** Provides default configuration */
public static EquoBasedStepBuilder createBuilder(Provisioner provisioner) {
return new EquoBasedStepBuilder(NAME, provisioner, defaultVersion(), EclipseCdtFormatterStep::apply, ImmutableMap.builder()) {
public static EquoBasedStepBuilder createBuilder(Provisioner provisioner, P2Provisioner p2Provisioner) {
return new EquoBasedStepBuilder(NAME, provisioner, p2Provisioner, defaultVersion(), EclipseCdtFormatterStep::apply, ImmutableMap.builder()) {
@Override
protected P2Model model(String version) {
var model = new P2Model();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 DiffPlug
* Copyright 2016-2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@
import com.diffplug.spotless.Jvm;
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.extra.EquoBasedStepBuilder;
import com.diffplug.spotless.extra.P2Provisioner;

import dev.equo.solstice.p2.P2Model;

Expand All @@ -39,8 +40,8 @@ public static String defaultVersion() {
return JVM_SUPPORT.getRecommendedFormatterVersion();
}

public static EquoBasedStepBuilder createBuilder(Provisioner provisioner) {
return new EquoBasedStepBuilder(NAME, provisioner, defaultVersion(), GrEclipseFormatterStep::apply, ImmutableMap.builder()) {
public static EquoBasedStepBuilder createBuilder(Provisioner provisioner, P2Provisioner p2Provisioner) {
return new EquoBasedStepBuilder(NAME, provisioner, p2Provisioner, defaultVersion(), GrEclipseFormatterStep::apply, ImmutableMap.builder()) {
@Override
protected P2Model model(String version) {
if (!version.startsWith("4.")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 DiffPlug
* Copyright 2016-2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,6 +25,7 @@
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.SerializedFunction;
import com.diffplug.spotless.extra.EquoBasedStepBuilder;
import com.diffplug.spotless.extra.P2Provisioner;

import dev.equo.solstice.p2.P2Model;

Expand All @@ -40,8 +41,8 @@ public static String defaultVersion() {
return JVM_SUPPORT.getRecommendedFormatterVersion();
}

public static EclipseJdtFormatterStep.Builder createBuilder(Provisioner provisioner) {
return new EclipseJdtFormatterStep.Builder(NAME, provisioner, defaultVersion(), EclipseJdtFormatterStep::apply, ImmutableMap.builder());
public static EclipseJdtFormatterStep.Builder createBuilder(Provisioner provisioner, P2Provisioner p2Provisioner) {
return new EclipseJdtFormatterStep.Builder(NAME, provisioner, p2Provisioner, defaultVersion(), EclipseJdtFormatterStep::apply, ImmutableMap.builder());
}

private static FormatterFunc apply(EquoBasedStepBuilder.State state) throws Exception {
Expand All @@ -59,10 +60,11 @@ public static class Builder extends EquoBasedStepBuilder {
Builder(
String formatterName,
Provisioner mavenProvisioner,
P2Provisioner p2Provisioner,
String defaultVersion,
SerializedFunction<State, FormatterFunc> stateToFormatter,
ImmutableMap.Builder<String, String> stepProperties) {
super(formatterName, mavenProvisioner, defaultVersion, stateToFormatter, stepProperties);
super(formatterName, mavenProvisioner, p2Provisioner, defaultVersion, stateToFormatter, stepProperties);
this.stepProperties = stepProperties;
}

Expand Down
Loading
Loading