Skip to content

Commit 08774a1

Browse files
committed
Defer dependency resolution using LazyFiles
1 parent a2ce4e2 commit 08774a1

File tree

6 files changed

+84
-47
lines changed

6 files changed

+84
-47
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public FormatterStep build() {
139139
mavenDeps.add("dev.equo.ide:solstice:1.8.1");
140140
mavenDeps.add("com.diffplug.durian:durian-swt.os:4.3.1");
141141
mavenDeps.addAll(query.getJarsOnMavenCentral());
142-
classpath.addAll(mavenProvisioner.provisionWithTransitives(false, mavenDeps));
142+
classpath.addAll(mavenProvisioner.provisionWithTransitives(false, mavenDeps).files());
143143
classpath.addAll(query.getJarsNotOnMavenCentral());
144144
for (var nested : NestedJars.inFiles(query.getJarsNotOnMavenCentral()).extractAllNestedJars()) {
145145
classpath.add(nested.getValue());

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public static JarState withoutTransitives(Collection<String> mavenCoordinates, P
129129
private static JarState provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates, Provisioner provisioner) throws IOException {
130130
Objects.requireNonNull(mavenCoordinates, "mavenCoordinates");
131131
Objects.requireNonNull(provisioner, "provisioner");
132-
Set<File> jars = provisioner.provisionWithTransitives(withTransitives, mavenCoordinates);
132+
Set<File> jars = provisioner.provisionWithTransitives(withTransitives, mavenCoordinates).files();
133133
if (jars.isEmpty()) {
134134
throw new NoSuchElementException("Resolved to an empty result: " + String.join(", ", mavenCoordinates));
135135
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2025 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.spotless;
17+
18+
import java.io.File;
19+
import java.util.Set;
20+
21+
/**
22+
* A lazy provider of a set of files, to allow build systems
23+
* to defer resolution of dependencies.
24+
*/
25+
@FunctionalInterface
26+
public interface LazyFiles {
27+
Set<File> files();
28+
29+
static LazyFiles of(Set<File> files) {
30+
return () -> files;
31+
}
32+
}

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 DiffPlug
2+
* Copyright 2016-2025 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.
@@ -15,27 +15,25 @@
1515
*/
1616
package com.diffplug.spotless;
1717

18-
import java.io.File;
1918
import java.util.Arrays;
2019
import java.util.Collection;
21-
import java.util.Set;
2220

2321
/**
2422
* Many spotless steps require third-party libraries, but we want to keep
2523
* Spotless' dependencies minimal.
2624
*/
2725
public interface Provisioner {
2826
/**
29-
* Given a set of Maven coordinates, returns a set of jars which include all
27+
* Given a set of Maven coordinates, returns a lazy set of jars which include all
3028
* of the specified coordinates and optionally their transitive dependencies.
3129
*/
32-
public default Set<File> provisionWithTransitives(boolean withTransitives, String... mavenCoordinates) {
30+
public default LazyFiles provisionWithTransitives(boolean withTransitives, String... mavenCoordinates) {
3331
return provisionWithTransitives(withTransitives, Arrays.asList(mavenCoordinates));
3432
}
3533

3634
/**
37-
* Given a set of Maven coordinates, returns a set of jars which include all
35+
* Given a set of Maven coordinates, returns a lazy set of jars which include all
3836
* of the specified coordinates and optionally their transitive dependencies.
3937
*/
40-
public Set<File> provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates);
38+
public LazyFiles provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates);
4139
}

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

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,27 @@
1515
*/
1616
package com.diffplug.gradle.spotless;
1717

18-
import java.io.File;
1918
import java.util.Collection;
2019
import java.util.HashMap;
2120
import java.util.Map;
22-
import java.util.Set;
2321

2422
import org.gradle.api.GradleException;
23+
import org.gradle.api.NamedDomainObjectProvider;
2524
import org.gradle.api.Project;
2625
import org.gradle.api.artifacts.Configuration;
2726
import org.gradle.api.artifacts.ConfigurationContainer;
2827
import org.gradle.api.artifacts.dsl.DependencyHandler;
2928
import org.gradle.api.attributes.Bundling;
3029
import org.gradle.api.attributes.Category;
3130
import org.gradle.api.attributes.java.TargetJvmEnvironment;
31+
import org.gradle.api.file.FileCollection;
3232
import org.gradle.api.initialization.dsl.ScriptHandler;
3333
import org.slf4j.Logger;
3434
import org.slf4j.LoggerFactory;
3535

3636
import com.diffplug.common.base.Unhandled;
3737
import com.diffplug.common.collect.ImmutableList;
38+
import com.diffplug.spotless.LazyFiles;
3839
import com.diffplug.spotless.Provisioner;
3940

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

6061
static class DedupingProvisioner implements Provisioner {
6162
private final Provisioner provisioner;
62-
private final Map<Request, Set<File>> cache = new HashMap<>();
63+
private final Map<Request, LazyFiles> cache = new HashMap<>();
6364

6465
DedupingProvisioner(Provisioner provisioner) {
6566
this.provisioner = provisioner;
6667
}
6768

6869
@Override
69-
public Set<File> provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates) {
70+
public LazyFiles provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates) {
7071
Request req = new Request(withTransitives, mavenCoordinates);
71-
Set<File> result;
72+
LazyFiles result;
7273
synchronized (cache) {
7374
result = cache.get(req);
7475
}
@@ -89,7 +90,7 @@ public Set<File> provisionWithTransitives(boolean withTransitives, Collection<St
8990
/** A child Provisioner which retries cached elements only. */
9091
final Provisioner cachedOnly = (withTransitives, mavenCoordinates) -> {
9192
Request req = new Request(withTransitives, mavenCoordinates);
92-
Set<File> result;
93+
LazyFiles result;
9394
synchronized (cache) {
9495
result = cache.get(req);
9596
}
@@ -113,22 +114,25 @@ static Provisioner forRootProjectBuildscript(Project project) {
113114
private static Provisioner forConfigurationContainer(Project project, ConfigurationContainer configurations, DependencyHandler dependencies) {
114115
return (withTransitives, mavenCoords) -> {
115116
try {
116-
Configuration config = configurations.create("spotless"
117-
+ new Request(withTransitives, mavenCoords).hashCode());
118-
mavenCoords.stream()
119-
.map(dependencies::create)
120-
.forEach(config.getDependencies()::add);
121-
config.setDescription(mavenCoords.toString());
122-
config.setTransitive(withTransitives);
123-
config.setCanBeConsumed(false);
124-
config.setVisible(false);
125-
config.attributes(attr -> {
126-
attr.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY));
127-
attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL));
128-
// Add this attribute for resolving Guava dependency, see https://github.com/google/guava/issues/6801.
129-
attr.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.getObjects().named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
130-
});
131-
return config.resolve();
117+
NamedDomainObjectProvider<Configuration> config = configurations.register(
118+
"spotless" + new Request(withTransitives, mavenCoords).hashCode(),
119+
files -> {
120+
mavenCoords.stream()
121+
.map(dependencies::create)
122+
.forEach(files.getDependencies()::add);
123+
files.setDescription(mavenCoords.toString());
124+
files.setTransitive(withTransitives);
125+
files.setCanBeConsumed(false);
126+
files.setVisible(false);
127+
files.attributes(attr -> {
128+
attr.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY));
129+
attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL));
130+
attr.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.getObjects().named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
131+
});
132+
});
133+
134+
final FileCollection configFiles = project.files(config);
135+
return configFiles::getFiles;
132136
} catch (Exception e) {
133137
String projName = project.getPath().substring(1).replace(':', '/');
134138
if (!projName.isEmpty()) {

testlib/src/main/java/com/diffplug/spotless/TestProvisioner.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Comparator;
2424
import java.util.HashMap;
2525
import java.util.Map;
26+
import java.util.Set;
2627
import java.util.function.Consumer;
2728
import java.util.function.Supplier;
2829

@@ -77,22 +78,24 @@ private static Provisioner createWithRepositories(Consumer<RepositoryHandler> re
7778
// Add this attribute for resolving Guava dependency, see https://github.com/google/guava/issues/6801.
7879
attr.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.getObjects().named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
7980
});
80-
try {
81-
return config.resolve();
82-
} catch (ResolveException e) {
83-
/* Provide Maven coordinates in exception message instead of static string 'detachedConfiguration' */
84-
throw new RuntimeException("Error resolving configuration: " + config.getDescription(), e);
85-
} finally {
86-
// delete the temp dir
81+
return (LazyFiles) () -> {
8782
try {
88-
java.nio.file.Files.walk(tempDir.toPath())
89-
.sorted(Comparator.reverseOrder())
90-
.map(Path::toFile)
91-
.forEach(File::delete);
92-
} catch (IOException e) {
93-
throw Errors.asRuntime(e);
83+
return config.resolve();
84+
} catch (ResolveException e) {
85+
/* Provide Maven coordinates in exception message instead of static string 'detachedConfiguration' */
86+
throw new RuntimeException("Error resolving configuration: " + config.getDescription(), e);
87+
} finally {
88+
// delete the temp dir
89+
try {
90+
java.nio.file.Files.walk(tempDir.toPath())
91+
.sorted(Comparator.reverseOrder())
92+
.map(Path::toFile)
93+
.forEach(File::delete);
94+
} catch (IOException e) {
95+
throw Errors.asRuntime(e);
96+
}
9497
}
95-
}
98+
};
9699
};
97100
}
98101

@@ -125,15 +128,15 @@ private static Provisioner caching(String name, Supplier<Provisioner> input) {
125128
// double-check that depcache pruning hasn't removed them since our cache cached them
126129
boolean needsToBeSet = result == null || !result.stream().allMatch(file -> file.exists() && file.isFile() && file.length() > 0);
127130
if (needsToBeSet) {
128-
result = ImmutableSet.copyOf(input.get().provisionWithTransitives(withTransitives, mavenCoords));
131+
result = ImmutableSet.copyOf(input.get().provisionWithTransitives(withTransitives, mavenCoords).files());
129132
cached.put(mavenCoords, result);
130133
try (ObjectOutputStream outputStream = new ObjectOutputStream(Files.asByteSink(cacheFile).openBufferedStream())) {
131134
outputStream.writeObject(cached);
132135
} catch (IOException e) {
133136
throw Errors.asRuntime(e);
134137
}
135138
}
136-
return result;
139+
return LazyFiles.of(result);
137140
}
138141
};
139142
}

0 commit comments

Comments
 (0)