Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public FormatterStep build() {
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(mavenProvisioner.provisionWithTransitives(false, mavenDeps).files());
classpath.addAll(query.getJarsNotOnMavenCentral());
for (var nested : NestedJars.inFiles(query.getJarsNotOnMavenCentral()).extractAllNestedJars()) {
classpath.add(nested.getValue());
Expand Down
2 changes: 1 addition & 1 deletion lib/src/main/java/com/diffplug/spotless/JarState.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public static JarState withoutTransitives(Collection<String> mavenCoordinates, P
private static JarState provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates, Provisioner provisioner) throws IOException {
Objects.requireNonNull(mavenCoordinates, "mavenCoordinates");
Objects.requireNonNull(provisioner, "provisioner");
Set<File> jars = provisioner.provisionWithTransitives(withTransitives, mavenCoordinates);
Set<File> jars = provisioner.provisionWithTransitives(withTransitives, mavenCoordinates).files();
if (jars.isEmpty()) {
throw new NoSuchElementException("Resolved to an empty result: " + String.join(", ", mavenCoordinates));
}
Expand Down
32 changes: 32 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/LazyFiles.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.spotless;

import java.io.File;
import java.util.Set;

/**
* A lazy provider of a set of files, to allow build systems
* to defer resolution of dependencies.
*/
@FunctionalInterface
public interface LazyFiles {
Set<File> files();

static LazyFiles of(Set<File> files) {
return () -> files;
}
}
12 changes: 5 additions & 7 deletions lib/src/main/java/com/diffplug/spotless/Provisioner.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 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.
Expand All @@ -15,27 +15,25 @@
*/
package com.diffplug.spotless;

import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

/**
* Many spotless steps require third-party libraries, but we want to keep
* Spotless' dependencies minimal.
*/
public interface Provisioner {
/**
* Given a set of Maven coordinates, returns a set of jars which include all
* Given a set of Maven coordinates, returns a lazy set of jars which include all
* of the specified coordinates and optionally their transitive dependencies.
*/
public default Set<File> provisionWithTransitives(boolean withTransitives, String... mavenCoordinates) {
public default LazyFiles provisionWithTransitives(boolean withTransitives, String... mavenCoordinates) {
return provisionWithTransitives(withTransitives, Arrays.asList(mavenCoordinates));
}

/**
* Given a set of Maven coordinates, returns a set of jars which include all
* Given a set of Maven coordinates, returns a lazy set of jars which include all
* of the specified coordinates and optionally their transitive dependencies.
*/
public Set<File> provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates);
public LazyFiles provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates);
}
6 changes: 5 additions & 1 deletion lib/src/test/java/com/diffplug/spotless/JarStateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import java.util.stream.Collectors;

import org.assertj.core.api.SoftAssertions;
Expand All @@ -42,7 +43,10 @@ class JarStateTest {

File b;

Provisioner provisioner = (withTransitives, deps) -> deps.stream().map(name -> name.equals("a") ? a : b).collect(Collectors.toSet());
Provisioner provisioner = (withTransitives, deps) -> {
Set<File> files = deps.stream().map(name -> name.equals("a") ? a : b).collect(Collectors.toSet());
return LazyFiles.of(files);
};

@BeforeEach
void setUp() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,27 @@
*/
package com.diffplug.gradle.spotless;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.gradle.api.GradleException;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.api.attributes.Bundling;
import org.gradle.api.attributes.Category;
import org.gradle.api.attributes.java.TargetJvmEnvironment;
import org.gradle.api.file.FileCollection;
import org.gradle.api.initialization.dsl.ScriptHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.diffplug.common.base.Unhandled;
import com.diffplug.common.collect.ImmutableList;
import com.diffplug.spotless.LazyFiles;
import com.diffplug.spotless.Provisioner;

/** Should be package-private. */
Expand All @@ -59,16 +60,16 @@ public DedupingProvisioner dedupingProvisioner(Project project) {

static class DedupingProvisioner implements Provisioner {
private final Provisioner provisioner;
private final Map<Request, Set<File>> cache = new HashMap<>();
private final Map<Request, LazyFiles> cache = new HashMap<>();

DedupingProvisioner(Provisioner provisioner) {
this.provisioner = provisioner;
}

@Override
public Set<File> provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates) {
public LazyFiles provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates) {
Request req = new Request(withTransitives, mavenCoordinates);
Set<File> result;
LazyFiles result;
synchronized (cache) {
result = cache.get(req);
}
Expand All @@ -89,7 +90,7 @@ public Set<File> provisionWithTransitives(boolean withTransitives, Collection<St
/** A child Provisioner which retries cached elements only. */
final Provisioner cachedOnly = (withTransitives, mavenCoordinates) -> {
Request req = new Request(withTransitives, mavenCoordinates);
Set<File> result;
LazyFiles result;
synchronized (cache) {
result = cache.get(req);
}
Expand All @@ -113,22 +114,24 @@ static Provisioner forRootProjectBuildscript(Project project) {
private static Provisioner forConfigurationContainer(Project project, ConfigurationContainer configurations, DependencyHandler dependencies) {
return (withTransitives, mavenCoords) -> {
try {
Configuration config = configurations.create("spotless"
+ new Request(withTransitives, mavenCoords).hashCode());
mavenCoords.stream()
.map(dependencies::create)
.forEach(config.getDependencies()::add);
config.setDescription(mavenCoords.toString());
config.setTransitive(withTransitives);
config.setCanBeConsumed(false);
config.setVisible(false);
config.attributes(attr -> {
attr.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY));
attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL));
// Add this attribute for resolving Guava dependency, see https://github.com/google/guava/issues/6801.
attr.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.getObjects().named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
});
return config.resolve();
NamedDomainObjectProvider<Configuration> config = configurations.register(
"spotless" + new Request(withTransitives, mavenCoords).hashCode(),
files -> {
mavenCoords.stream()
.map(dependencies::create)
.forEach(files.getDependencies()::add);
files.setDescription(mavenCoords.toString());
files.setTransitive(withTransitives);
files.setCanBeConsumed(false);
files.setVisible(false);
files.attributes(attr -> {
attr.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY));
attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL));
attr.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.getObjects().named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
});
});
final FileCollection configFiles = project.files(config);
return configFiles::getFiles;
} catch (Exception e) {
String projName = project.getPath().substring(1).replace(':', '/');
if (!projName.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;

import com.diffplug.spotless.LazyFiles;

public class ArtifactResolver {

private static final Exclusion EXCLUDE_ALL_TRANSITIVES = new Exclusion("*", "*", "*", "*");
Expand All @@ -60,7 +62,7 @@ public ArtifactResolver(RepositorySystem repositorySystem, RepositorySystemSessi
* Given a set of maven coordinates, returns a set of jars which include all
* of the specified coordinates and optionally their transitive dependencies.
*/
public Set<File> resolve(boolean withTransitives, Collection<String> mavenCoordinates) {
public LazyFiles resolve(boolean withTransitives, Collection<String> mavenCoordinates) {
Collection<Exclusion> excludeTransitive = new ArrayList<>(1);
if (!withTransitives) {
excludeTransitive.add(EXCLUDE_ALL_TRANSITIVES);
Expand All @@ -73,12 +75,13 @@ public Set<File> resolve(boolean withTransitives, Collection<String> mavenCoordi
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
DependencyResult dependencyResult = resolveDependencies(dependencyRequest);

return dependencyResult.getArtifactResults()
Set<File> jars = dependencyResult.getArtifactResults()
.stream()
.peek(this::logResolved)
.map(ArtifactResult::getArtifact)
.map(Artifact::getFile)
.collect(toSet());
return LazyFiles.of(jars);
}

private DependencyResult resolveDependencies(DependencyRequest dependencyRequest) {
Expand Down
34 changes: 18 additions & 16 deletions testlib/src/main/java/com/diffplug/spotless/TestProvisioner.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,24 @@ private static Provisioner createWithRepositories(Consumer<RepositoryHandler> re
// Add this attribute for resolving Guava dependency, see https://github.com/google/guava/issues/6801.
attr.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.getObjects().named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
});
try {
return config.resolve();
} catch (ResolveException e) {
/* Provide Maven coordinates in exception message instead of static string 'detachedConfiguration' */
throw new RuntimeException("Error resolving configuration: " + config.getDescription(), e);
} finally {
// delete the temp dir
return (LazyFiles) () -> {
try {
java.nio.file.Files.walk(tempDir.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException e) {
throw Errors.asRuntime(e);
return config.resolve();
} catch (ResolveException e) {
/* Provide Maven coordinates in exception message instead of static string 'detachedConfiguration' */
throw new RuntimeException("Error resolving configuration: " + config.getDescription(), e);
} finally {
// delete the temp dir
try {
java.nio.file.Files.walk(tempDir.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException e) {
throw Errors.asRuntime(e);
}
}
}
};
};
}

Expand Down Expand Up @@ -125,15 +127,15 @@ private static Provisioner caching(String name, Supplier<Provisioner> input) {
// double-check that depcache pruning hasn't removed them since our cache cached them
boolean needsToBeSet = result == null || !result.stream().allMatch(file -> file.exists() && file.isFile() && file.length() > 0);
if (needsToBeSet) {
result = ImmutableSet.copyOf(input.get().provisionWithTransitives(withTransitives, mavenCoords));
result = ImmutableSet.copyOf(input.get().provisionWithTransitives(withTransitives, mavenCoords).files());
cached.put(mavenCoords, result);
try (ObjectOutputStream outputStream = new ObjectOutputStream(Files.asByteSink(cacheFile).openBufferedStream())) {
outputStream.writeObject(cached);
} catch (IOException e) {
throw Errors.asRuntime(e);
}
}
return result;
return LazyFiles.of(result);
}
};
}
Expand Down
14 changes: 9 additions & 5 deletions testlib/src/test/java/com/diffplug/spotless/ProvisionerTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2021 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.
Expand All @@ -17,6 +17,7 @@

import java.io.File;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

import org.assertj.core.api.Assertions;
Expand All @@ -25,12 +26,15 @@
class ProvisionerTest {
@Test
void testManipulation() {
Provisioner provisioner = (withTransitives, deps) -> deps.stream().map(File::new).collect(Collectors.toSet());
Assertions.assertThat(provisioner.provisionWithTransitives(true, "a"))
Provisioner provisioner = (withTransitives, deps) -> {
Set<File> files = deps.stream().map(File::new).collect(Collectors.toSet());
return LazyFiles.of(files);
};
Assertions.assertThat(provisioner.provisionWithTransitives(true, "a").files())
.containsExactlyInAnyOrder(new File("a"));
Assertions.assertThat(provisioner.provisionWithTransitives(true, "a", "a"))
Assertions.assertThat(provisioner.provisionWithTransitives(true, "a", "a").files())
.containsExactlyInAnyOrder(new File("a"));
Assertions.assertThat(provisioner.provisionWithTransitives(true, Arrays.asList("a", "a")))
Assertions.assertThat(provisioner.provisionWithTransitives(true, Arrays.asList("a", "a")).files())
.containsExactlyInAnyOrder(new File("a"));
}
}
Loading