Skip to content

Commit 24d1ea5

Browse files
authored
Resolve dependencies per-project (#983)
2 parents 7cc87da + 33fba68 commit 24d1ea5

17 files changed

+206
-164
lines changed

plugin-gradle/CHANGES.md

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

55
## [Unreleased]
66
### Changed
7-
* **BREAKING** Previously, many projects required `buildscript { repositories { mavenCentral() }}` at the top of their root project, because Spotless resolved its dependencies using the buildscript repositories. Spotless now resolves its dependencies from the normal project repositories of the root project, which means that you can remove the `buildscript {}` block, but you still need `repositories { mavenCentral() }` (or similar) in the root project.
7+
* **BREAKING** Previously, many projects required `buildscript { repositories { mavenCentral() }}` at the top of their root project, because Spotless resolved its dependencies using the buildscript repositories. Spotless now resolves its dependencies from the normal project repositories of each project with a `spotless {...}` block. This means that you can remove the `buildscript {}` block, but you still need a `repositories { mavenCentral() }` (or similar) in each project which is using Spotless.
88
* **BREAKING** `createIndepentApplyTask(String taskName)` now requires that `taskName` does not end with `Apply`
99
* Bump minimum required Gradle from `6.1` to `6.1.1`.
1010

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public FormatExtension(SpotlessExtension spotless) {
7575
}
7676

7777
protected final Provisioner provisioner() {
78-
return spotless.getRegisterDependenciesTask().rootProvisioner;
78+
return spotless.getRegisterDependenciesTask().getTaskService().get().provisionerFor(spotless.project);
7979
}
8080

8181
private String formatName() {
@@ -733,11 +733,9 @@ protected void setupTask(SpotlessTask task) {
733733
}
734734
task.setSteps(steps);
735735
task.setLineEndingsPolicy(getLineEndings().createPolicy(getProject().getProjectDir(), () -> totalTarget));
736-
if (spotless.project != spotless.project.getRootProject()) {
737-
spotless.getRegisterDependenciesTask().hookSubprojectTask(task);
738-
}
736+
spotless.getRegisterDependenciesTask().hookSubprojectTask(task);
739737
if (getRatchetFrom() != null) {
740-
task.setupRatchet(spotless.getRegisterDependenciesTask().getGitRatchet().get(), getRatchetFrom());
738+
task.setupRatchet(getRatchetFrom());
741739
}
742740
}
743741

@@ -763,7 +761,7 @@ public SpotlessApply createIndependentApplyTask(String taskName) {
763761
Preconditions.checkArgument(!taskName.endsWith(SpotlessExtension.APPLY), "Task name must not end with " + SpotlessExtension.APPLY);
764762
// create and setup the task
765763
SpotlessTaskImpl spotlessTask = spotless.project.getTasks().create(taskName + SpotlessTaskService.INDEPENDENT_HELPER, SpotlessTaskImpl.class);
766-
spotlessTask.init(spotless.getTaskService());
764+
spotlessTask.init(spotless.getRegisterDependenciesTask().getTaskService());
767765
setupTask(spotlessTask);
768766
// enforce the clean ordering
769767
Task clean = spotless.project.getTasks().getByName(BasePlugin.CLEAN_TASK_NAME);

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,10 @@
1919

2020
import javax.annotation.Nullable;
2121

22-
import org.gradle.api.services.BuildService;
23-
import org.gradle.api.services.BuildServiceParameters;
24-
import org.gradle.tooling.events.FinishEvent;
25-
import org.gradle.tooling.events.OperationCompletionListener;
26-
2722
import com.diffplug.spotless.extra.GitRatchet;
2823

2924
/** Gradle implementation of GitRatchet. */
30-
public abstract class GitRatchetGradle extends GitRatchet<File> implements BuildService<BuildServiceParameters.None>, OperationCompletionListener {
25+
public class GitRatchetGradle extends GitRatchet<File> {
3126
@Override
3227
protected File getDir(File project) {
3328
return project;
@@ -37,9 +32,4 @@ protected File getDir(File project) {
3732
protected @Nullable File getParent(File project) {
3833
return project.getParentFile();
3934
}
40-
41-
@Override
42-
public void onFinish(FinishEvent finishEvent) {
43-
// NOOP
44-
}
4535
}

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

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,49 +27,45 @@
2727
import org.gradle.api.Project;
2828
import org.gradle.api.artifacts.Configuration;
2929

30-
import com.diffplug.common.base.Preconditions;
3130
import com.diffplug.common.collect.ImmutableList;
3231
import com.diffplug.spotless.Provisioner;
3332

3433
/** Should be package-private. */
3534
class GradleProvisioner {
3635
private GradleProvisioner() {}
3736

38-
/** The provisioner used for the root project. */
39-
static class RootProvisioner implements Provisioner {
40-
private final Project rootProject;
37+
static Provisioner newDedupingProvisioner(Project project) {
38+
return new DedupingProvisioner(project);
39+
}
40+
41+
static class DedupingProvisioner implements Provisioner {
42+
private final Project project;
4143
private final Map<Request, Set<File>> cache = new HashMap<>();
4244

43-
RootProvisioner(Project rootProject) {
44-
Preconditions.checkArgument(rootProject == rootProject.getRootProject());
45-
this.rootProject = rootProject;
45+
DedupingProvisioner(Project project) {
46+
this.project = project;
4647
}
4748

4849
@Override
4950
public Set<File> provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates) {
5051
Request req = new Request(withTransitives, mavenCoordinates);
51-
Set<File> result;
52-
synchronized (cache) {
53-
result = cache.get(req);
54-
}
52+
Set<File> result = cache.get(req);
5553
if (result != null) {
5654
return result;
5755
} else {
58-
synchronized (cache) {
59-
result = cache.get(req);
60-
if (result != null) {
61-
return result;
62-
} else {
63-
result = GradleProvisioner.forProject(rootProject).provisionWithTransitives(req.withTransitives, req.mavenCoords);
64-
cache.put(req, result);
65-
return result;
66-
}
56+
result = cache.get(req);
57+
if (result != null) {
58+
return result;
59+
} else {
60+
result = forProject(project).provisionWithTransitives(req.withTransitives, req.mavenCoords);
61+
cache.put(req, result);
62+
return result;
6763
}
6864
}
6965
}
7066
}
7167

72-
static Provisioner forProject(Project project) {
68+
private static Provisioner forProject(Project project) {
7369
Objects.requireNonNull(project);
7470
return (withTransitives, mavenCoords) -> {
7571
try {
@@ -82,10 +78,13 @@ static Provisioner forProject(Project project) {
8278
config.setTransitive(withTransitives);
8379
return config.resolve();
8480
} catch (Exception e) {
85-
String projName = project.getPath();
81+
String projName = project.getPath().substring(1).replace(':', '/');
82+
if (!projName.isEmpty()) {
83+
projName = projName + "/";
84+
}
8685
logger.log(
8786
Level.SEVERE,
88-
"You probably need to add a repository containing the '" + mavenCoords + "' artifact in the 'build.gradle' of the " + projName + " project.\n" +
87+
"You need to add a repository containing the '" + mavenCoords + "' artifact in '" + projName + "build.gradle'.\n" +
8988
"E.g.: 'repositories { mavenCentral() }'",
9089
e);
9190
throw e;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ static void performHook(SpotlessTaskImpl spotlessTask) {
4242
}
4343
if (spotlessTask.getTarget().contains(file)) {
4444
try (Formatter formatter = spotlessTask.buildFormatter()) {
45-
if (spotlessTask.ratchet != null) {
46-
if (spotlessTask.ratchet.isClean(spotlessTask.getProjectDir().get().getAsFile(), spotlessTask.rootTreeSha, file)) {
45+
if (spotlessTask.getRatchet() != null) {
46+
if (spotlessTask.getRatchet().isClean(spotlessTask.getProjectDir().get().getAsFile(), spotlessTask.getRootTreeSha(), file)) {
4747
dumpIsClean();
4848
return;
4949
}

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

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,19 @@
1818
import java.io.File;
1919
import java.io.IOException;
2020
import java.nio.charset.StandardCharsets;
21-
import java.util.ArrayList;
22-
import java.util.List;
2321

2422
import javax.inject.Inject;
2523

2624
import org.gradle.api.DefaultTask;
27-
import org.gradle.api.execution.TaskExecutionGraph;
2825
import org.gradle.api.provider.Property;
29-
import org.gradle.api.provider.Provider;
30-
import org.gradle.api.tasks.Input;
26+
import org.gradle.api.services.BuildServiceRegistry;
3127
import org.gradle.api.tasks.Internal;
3228
import org.gradle.api.tasks.OutputFile;
3329
import org.gradle.api.tasks.TaskAction;
3430
import org.gradle.build.event.BuildEventsListenerRegistry;
3531

3632
import com.diffplug.common.base.Preconditions;
3733
import com.diffplug.common.io.Files;
38-
import com.diffplug.spotless.FormatterStep;
3934

4035
/**
4136
* NOT AN END-USER TASK, DO NOT USE FOR ANYTHING!
@@ -50,26 +45,17 @@
5045
public abstract class RegisterDependenciesTask extends DefaultTask {
5146
static final String TASK_NAME = "spotlessInternalRegisterDependencies";
5247

53-
@Input
54-
public List<FormatterStep> getSteps() {
55-
List<FormatterStep> allSteps = new ArrayList<>();
56-
TaskExecutionGraph taskGraph = getProject().getGradle().getTaskGraph();
57-
tasks.stream()
58-
.filter(taskGraph::hasTask)
59-
.sorted()
60-
.forEach(task -> allSteps.addAll(task.getSteps()));
61-
return allSteps;
62-
}
63-
64-
private List<SpotlessTask> tasks = new ArrayList<>();
65-
66-
@Internal
67-
public List<SpotlessTask> getTasks() {
68-
return tasks;
69-
}
70-
7148
void hookSubprojectTask(SpotlessTask task) {
72-
tasks.add(task);
49+
// TODO: in the future, we might use this hook to add an optional perf improvement
50+
// spotlessRoot {
51+
// java { googleJavaFormat('1.2') }
52+
// ...etc
53+
// }
54+
// The point would be to reuse configurations from the root project,
55+
// with the restriction that you have to declare every formatter in
56+
// the root, and you'd get an error if you used a formatter somewhere
57+
// which you didn't declare in the root. That's a problem for the future
58+
// though, not today!
7359
task.dependsOn(this);
7460
}
7561

@@ -80,30 +66,23 @@ public File getUnitOutput() {
8066
return unitOutput;
8167
}
8268

83-
GradleProvisioner.RootProvisioner rootProvisioner;
84-
85-
@Internal
86-
public GradleProvisioner.RootProvisioner getRootProvisioner() {
87-
return rootProvisioner;
88-
}
89-
9069
void setup() {
9170
Preconditions.checkArgument(getProject().getRootProject() == getProject(), "Can only be used on the root project");
9271
unitOutput = new File(getProject().getBuildDir(), "tmp/spotless-register-dependencies");
93-
rootProvisioner = new GradleProvisioner.RootProvisioner(getProject());
94-
Provider<GitRatchetGradle> gitRatchetProvider = getProject().getGradle().getSharedServices().registerIfAbsent("GitRatchetGradle", GitRatchetGradle.class, unused -> {});
95-
getBuildEventsListenerRegistry().onTaskCompletion(gitRatchetProvider);
96-
getGitRatchet().set(gitRatchetProvider);
72+
73+
BuildServiceRegistry buildServices = getProject().getGradle().getSharedServices();
74+
getTaskService().set(buildServices.registerIfAbsent("SpotlessTaskService", SpotlessTaskService.class, spec -> {}));
75+
getBuildEventsListenerRegistry().onTaskCompletion(getTaskService());
9776
}
9877

9978
@TaskAction
10079
public void trivialFunction() throws IOException {
10180
Files.createParentDirs(unitOutput);
102-
Files.write(Integer.toString(getSteps().size()), unitOutput, StandardCharsets.UTF_8);
81+
Files.write(Integer.toString(1), unitOutput, StandardCharsets.UTF_8);
10382
}
10483

10584
@Internal
106-
public abstract Property<GitRatchetGradle> getGitRatchet();
85+
abstract Property<SpotlessTaskService> getTaskService();
10786

10887
@Inject
10988
protected abstract BuildEventsListenerRegistry getBuildEventsListenerRegistry();

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.gradle.api.Action;
2828
import org.gradle.api.GradleException;
2929
import org.gradle.api.Project;
30-
import org.gradle.api.provider.Provider;
3130

3231
import com.diffplug.spotless.LineEnding;
3332

@@ -47,8 +46,6 @@ protected SpotlessExtension(Project project) {
4746
this.project = requireNonNull(project);
4847
}
4948

50-
abstract Provider<SpotlessTaskService> getTaskService();
51-
5249
abstract RegisterDependenciesTask getRegisterDependenciesTask();
5350

5451
/** Line endings (if any). */

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,11 @@
1919
import org.gradle.api.Project;
2020
import org.gradle.api.plugins.BasePlugin;
2121
import org.gradle.api.plugins.JavaBasePlugin;
22-
import org.gradle.api.provider.Provider;
2322
import org.gradle.api.tasks.TaskContainer;
2423
import org.gradle.api.tasks.TaskProvider;
2524

2625
public class SpotlessExtensionImpl extends SpotlessExtension {
2726
private final TaskProvider<RegisterDependenciesTask> registerDependenciesTask;
28-
private final Provider<SpotlessTaskService> taskService;
2927

3028
public SpotlessExtensionImpl(Project project) {
3129
super(project);
@@ -54,13 +52,6 @@ public SpotlessExtensionImpl(Project project) {
5452
.configure(task -> task.dependsOn(rootCheckTask));
5553
}
5654
});
57-
58-
taskService = project.getGradle().getSharedServices().registerIfAbsent("SpotlessTaskService", SpotlessTaskService.class, spec -> {});
59-
}
60-
61-
@Override
62-
Provider<SpotlessTaskService> getTaskService() {
63-
return taskService;
6455
}
6556

6657
final TaskProvider<?> rootCheckTask, rootApplyTask, rootDiagnoseTask;
@@ -78,7 +69,7 @@ protected void createFormatTasks(String name, FormatExtension formatExtension) {
7869
// create the SpotlessTask
7970
String taskName = EXTENSION + SpotlessPlugin.capitalize(name);
8071
TaskProvider<SpotlessTaskImpl> spotlessTask = tasks.register(taskName, SpotlessTaskImpl.class, task -> {
81-
task.init(taskService);
72+
task.init(getRegisterDependenciesTask().getTaskService());
8273
task.setEnabled(!isIdeHook);
8374
// clean removes the SpotlessCache, so we have to run after clean
8475
task.mustRunAfter(cleanTask);

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.gradle.api.DefaultTask;
3030
import org.gradle.api.file.DirectoryProperty;
3131
import org.gradle.api.file.FileCollection;
32+
import org.gradle.api.provider.Property;
3233
import org.gradle.api.tasks.Input;
3334
import org.gradle.api.tasks.InputFiles;
3435
import org.gradle.api.tasks.Internal;
@@ -44,6 +45,9 @@
4445
import com.diffplug.spotless.LineEnding;
4546

4647
public abstract class SpotlessTask extends DefaultTask {
48+
@Internal
49+
abstract Property<SpotlessTaskService> getTaskService();
50+
4751
// set by SpotlessExtension, but possibly overridden by FormatExtension
4852
protected String encoding = "UTF-8";
4953

@@ -56,7 +60,7 @@ public void setEncoding(String encoding) {
5660
this.encoding = Objects.requireNonNull(encoding);
5761
}
5862

59-
protected LineEnding.Policy lineEndingsPolicy;
63+
protected transient LineEnding.Policy lineEndingsPolicy;
6064

6165
@Input
6266
public LineEnding.Policy getLineEndingsPolicy() {
@@ -69,21 +73,21 @@ public void setLineEndingsPolicy(LineEnding.Policy lineEndingsPolicy) {
6973

7074
/*** API which performs git up-to-date tasks. */
7175
@Nullable
72-
GitRatchetGradle ratchet;
76+
private transient GitRatchetGradle ratchet;
7377
/** The sha of the tree at repository root, used for determining if an individual *file* is clean according to git. */
74-
ObjectId rootTreeSha;
78+
private transient ObjectId rootTreeSha;
7579
/**
7680
* The sha of the tree at the root of *this project*, used to determine if the git baseline has changed within this folder.
7781
* Using a more fine-grained tree (rather than the project root) allows Gradle to mark more subprojects as up-to-date
7882
* compared to using the project root.
7983
*/
80-
private ObjectId subtreeSha = ObjectId.zeroId();
84+
private transient ObjectId subtreeSha = ObjectId.zeroId();
8185

82-
public void setupRatchet(GitRatchetGradle gitRatchet, String ratchetFrom) {
83-
ratchet = gitRatchet;
86+
public void setupRatchet(String ratchetFrom) {
87+
ratchet = getTaskService().get().getRatchet();
8488
File projectDir = getProjectDir().get().getAsFile();
85-
rootTreeSha = gitRatchet.rootTreeShaOf(projectDir, ratchetFrom);
86-
subtreeSha = gitRatchet.subtreeShaOf(projectDir, rootTreeSha);
89+
rootTreeSha = ratchet.rootTreeShaOf(projectDir, ratchetFrom);
90+
subtreeSha = ratchet.subtreeShaOf(projectDir, rootTreeSha);
8791
}
8892

8993
@Internal
@@ -139,7 +143,7 @@ public File getOutputDirectory() {
139143
return outputDirectory;
140144
}
141145

142-
protected List<FormatterStep> steps = new ArrayList<>();
146+
protected transient List<FormatterStep> steps = new ArrayList<>();
143147

144148
@Input
145149
public List<FormatterStep> getSteps() {

0 commit comments

Comments
 (0)