Skip to content

Commit 289f916

Browse files
authored
Merge pull request #576 from bigdaz/dd/incremental
Separate the core `SpotlessTask` from check and apply
2 parents d1c0c5d + 5a4fc52 commit 289f916

18 files changed

+500
-329
lines changed

.circleci/config.yml

+7-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ anchors:
88
GRADLE_OPTS: "-Dorg.gradle.workers.max=2" # and we're only allowed to use 2 vCPUs
99
docker:
1010
- image: cimg/openjdk:8.0
11+
env_gradle_large: &env_gradle_large
12+
<< : *env_gradle
13+
resource_class: large # https://circleci.com/docs/2.0/configuration-reference/#resource_class
14+
GRADLE_OPTS: "-Dorg.gradle.workers.max=4"
1115

1216
restore_cache_wrapper: &restore_cache_wrapper
1317
restore_cache:
@@ -41,7 +45,7 @@ version: 2
4145
jobs:
4246
# gradlew spotlessCheck assemble testClasses
4347
assemble_testClasses:
44-
<<: *env_gradle
48+
<<: *env_gradle_large
4549
steps:
4650
- checkout
4751
- *restore_cache_wrapper
@@ -64,10 +68,10 @@ jobs:
6468
key: gradle-deps2-{{ checksum "build.gradle" }}-{{ checksum "gradle.properties" }}
6569
# JRE8: export SPOTLESS_EXCLUDE_MAVEN=true && ./gradlew check
6670
test_nomaven_8:
67-
<<: *env_gradle
71+
<<: *env_gradle_large
6872
<<: *test_nomaven
6973
test_nomaven_11:
70-
<<: *env_gradle
74+
<<: *env_gradle_large
7175
docker:
7276
- image: cimg/openjdk:11.0
7377
environment: # java 11 does play nice with containers, doesn't need special settings

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This document is intended for Spotless developers.
1010
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
1111

1212
## [Unreleased]
13+
### Fixed
14+
* `PaddedCell.DirtyState::writeCanonicalTo(File)` can now create a new file if necessary (previously required to overwrite an existing file) ([#576](https://github.com/diffplug/spotless/pull/576)).
1315

1416
## [1.30.0] - 2020-05-11
1517
### Added

gradle/java-setup.gradle

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ spotbugs {
3636
toolVersion = VER_SPOTBUGS
3737
ignoreFailures = false // bug free or it doesn't ship!
3838
reportsDir = file('build/spotbugs')
39-
effort = 'max' // min|default|max
4039
reportLevel = 'medium' // low|medium|high (low = sensitive to even minor mistakes)
4140
omitVisitors = [] // bugs that we want to ignore
4241
}
@@ -46,6 +45,9 @@ tasks.named('spotbugsTest') {
4645
tasks.named('spotbugsMain') {
4746
// only run on Java 8 (no benefit to running twice)
4847
enabled = org.gradle.api.JavaVersion.current() == org.gradle.api.JavaVersion.VERSION_1_8
48+
reports {
49+
html.enabled = true
50+
}
4951
}
5052
dependencies {
5153
compileOnly 'net.jcip:jcip-annotations:1.0'

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.io.IOException;
2222
import java.io.OutputStream;
2323
import java.nio.file.Files;
24-
import java.nio.file.StandardOpenOption;
2524
import java.util.ArrayList;
2625
import java.util.Arrays;
2726
import java.util.Collections;
@@ -258,7 +257,7 @@ private byte[] canonicalBytes() {
258257
}
259258

260259
public void writeCanonicalTo(File file) throws IOException {
261-
Files.write(file.toPath(), canonicalBytes(), StandardOpenOption.TRUNCATE_EXISTING);
260+
Files.write(file.toPath(), canonicalBytes());
262261
}
263262

264263
public void writeCanonicalTo(OutputStream out) throws IOException {

plugin-gradle/CHANGES.md

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).
44

55
## [Unreleased]
6+
**TLDR: This version improves performance and adds support for the local Gradle Build Cache. You will not need to make any changes in your buildscript.** It is a breaking change only for a few users who have built *other* plugins on top of this one.
7+
8+
### Added
9+
* Support for the gradle build cache. ([#576](https://github.com/diffplug/spotless/pull/576))
10+
* The local cache will work great, but the remote cache will always miss until [#566](https://github.com/diffplug/spotless/issues/566) is resolved.
11+
### Removed
12+
* (Power users only) `void SpotlessTask::setCheck()` and `setApply()` have been removed. ([#576](https://github.com/diffplug/spotless/pull/576))
13+
* Previously, the `check` and `apply` tasks were just marker tasks, and they called `setCheck` and `setApply` on the "worker" task. Now `check` and `apply` are real tasks in their own right, so the marker-task kludge is no longer necessary.
14+
### Changed
15+
* (Power users only) `SpotlessTask FormatExtension::createIndependentTask` has been removed, and replaced with `SpotlessApply::createIndependentApplyTask`. ([#576](https://github.com/diffplug/spotless/pull/576))
616

717
## [3.30.0] - 2020-05-11
818
### Added

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

+11-10
Original file line numberDiff line numberDiff line change
@@ -613,30 +613,31 @@ protected Project getProject() {
613613
}
614614

615615
/**
616-
* Creates an independent {@link SpotlessTask} for (very) unusual circumstances.
616+
* Creates an independent {@link SpotlessApply} for (very) unusual circumstances.
617617
*
618618
* Most users will not want this method. In the rare case that you want to create
619-
* a SpotlessTask which is independent of the normal Spotless machinery, this will
619+
* a `SpotlessApply` which is independent of the normal Spotless machinery, this will
620620
* let you do that.
621621
*
622-
* The returned task will have no dependencies on any other task.
623-
* You need to call {@link SpotlessTask#setApply()} and/or {@link SpotlessTask#setCheck()}
624-
* on the return value, otherwise you will get a runtime error when the task tries to run.
622+
* The returned task will not be hooked up to the global `spotlessApply`, and there will be no corresponding `check` task.
625623
*
626624
* NOTE: does not respect the rarely-used [`spotlessFiles` property](https://github.com/diffplug/spotless/blob/b7f8c551a97dcb92cc4b0ee665448da5013b30a3/plugin-gradle/README.md#can-i-apply-spotless-to-specific-files).
627625
*/
628-
public SpotlessTask createIndependentTask(String taskName) {
626+
public SpotlessApply createIndependentApplyTask(String taskName) {
629627
// create and setup the task
630-
SpotlessTask spotlessTask = root.project.getTasks().create(taskName, SpotlessTask.class);
628+
SpotlessTask spotlessTask = root.project.getTasks().create(taskName + "Helper", SpotlessTask.class);
631629
setupTask(spotlessTask);
632-
633630
// enforce the clean ordering
634631
Task clean = root.project.getTasks().getByName(BasePlugin.CLEAN_TASK_NAME);
635632
spotlessTask.mustRunAfter(clean);
636-
637633
// ignore the filePatterns
638634
spotlessTask.setFilePatterns("");
635+
// create the apply task
636+
SpotlessApply applyTask = root.project.getTasks().create(taskName, SpotlessApply.class);
637+
applyTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory());
638+
applyTask.source = spotlessTask;
639+
applyTask.dependsOn(spotlessTask);
639640

640-
return spotlessTask;
641+
return applyTask;
641642
}
642643
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2016 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.gradle.spotless;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.nio.file.Files;
21+
import java.nio.file.StandardCopyOption;
22+
23+
import org.gradle.api.DefaultTask;
24+
import org.gradle.api.file.ConfigurableFileTree;
25+
import org.gradle.api.file.FileVisitDetails;
26+
import org.gradle.api.file.FileVisitor;
27+
import org.gradle.api.tasks.InputDirectory;
28+
import org.gradle.api.tasks.PathSensitive;
29+
import org.gradle.api.tasks.PathSensitivity;
30+
import org.gradle.api.tasks.TaskAction;
31+
32+
public class SpotlessApply extends DefaultTask {
33+
SpotlessTask source;
34+
private File spotlessOutDirectory;
35+
36+
@PathSensitive(PathSensitivity.RELATIVE)
37+
@InputDirectory
38+
public File getSpotlessOutDirectory() {
39+
return spotlessOutDirectory;
40+
}
41+
42+
public void setSpotlessOutDirectory(File spotlessOutDirectory) {
43+
this.spotlessOutDirectory = spotlessOutDirectory;
44+
}
45+
46+
@TaskAction
47+
public void performAction() {
48+
ConfigurableFileTree files = getProject().fileTree(spotlessOutDirectory);
49+
if (files.isEmpty()) {
50+
getState().setDidWork(source.getDidWork());
51+
} else {
52+
files.visit(new FileVisitor() {
53+
@Override
54+
public void visitDir(FileVisitDetails fileVisitDetails) {
55+
56+
}
57+
58+
@Override
59+
public void visitFile(FileVisitDetails fileVisitDetails) {
60+
String path = fileVisitDetails.getPath();
61+
File originalSource = new File(getProject().getProjectDir(), path);
62+
try {
63+
getLogger().debug("Copying " + fileVisitDetails.getFile() + " to " + originalSource);
64+
Files.copy(fileVisitDetails.getFile().toPath(), originalSource.toPath(), StandardCopyOption.REPLACE_EXISTING);
65+
} catch (IOException e) {
66+
throw new RuntimeException(e);
67+
}
68+
}
69+
});
70+
}
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2016 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.gradle.spotless;
17+
18+
import java.io.File;
19+
import java.util.ArrayList;
20+
import java.util.Collections;
21+
import java.util.List;
22+
23+
import org.gradle.api.DefaultTask;
24+
import org.gradle.api.GradleException;
25+
import org.gradle.api.file.ConfigurableFileTree;
26+
import org.gradle.api.file.FileVisitDetails;
27+
import org.gradle.api.file.FileVisitor;
28+
import org.gradle.api.tasks.InputDirectory;
29+
import org.gradle.api.tasks.PathSensitive;
30+
import org.gradle.api.tasks.PathSensitivity;
31+
import org.gradle.api.tasks.TaskAction;
32+
33+
import com.diffplug.spotless.Formatter;
34+
import com.diffplug.spotless.extra.integration.DiffMessageFormatter;
35+
36+
public class SpotlessCheck extends DefaultTask {
37+
SpotlessTask source;
38+
private File spotlessOutDirectory;
39+
40+
@PathSensitive(PathSensitivity.RELATIVE)
41+
@InputDirectory
42+
public File getSpotlessOutDirectory() {
43+
return spotlessOutDirectory;
44+
}
45+
46+
public void setSpotlessOutDirectory(File spotlessOutDirectory) {
47+
this.spotlessOutDirectory = spotlessOutDirectory;
48+
}
49+
50+
@TaskAction
51+
public void performAction() throws Exception {
52+
ConfigurableFileTree files = getProject().fileTree(spotlessOutDirectory);
53+
if (files.isEmpty()) {
54+
getState().setDidWork(source.getDidWork());
55+
} else {
56+
List<File> problemFiles = new ArrayList<>();
57+
files.visit(new FileVisitor() {
58+
@Override
59+
public void visitDir(FileVisitDetails fileVisitDetails) {
60+
61+
}
62+
63+
@Override
64+
public void visitFile(FileVisitDetails fileVisitDetails) {
65+
String path = fileVisitDetails.getPath();
66+
File originalSource = new File(getProject().getProjectDir(), path);
67+
problemFiles.add(originalSource);
68+
}
69+
});
70+
71+
if (!problemFiles.isEmpty()) {
72+
Formatter formatter = source.buildFormatter();
73+
Collections.sort(problemFiles);
74+
throw formatViolationsFor(formatter, problemFiles);
75+
}
76+
}
77+
}
78+
79+
/** Returns an exception which indicates problem files nicely. */
80+
private static GradleException formatViolationsFor(Formatter formatter, List<File> problemFiles) {
81+
return new GradleException(DiffMessageFormatter.builder()
82+
.runToFix("Run 'gradlew spotlessApply' to fix these violations.")
83+
.formatter(formatter)
84+
.problemFiles(problemFiles)
85+
.getMessage());
86+
}
87+
88+
}

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

+15-25
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,11 @@
2727
import org.gradle.api.GradleException;
2828
import org.gradle.api.Project;
2929
import org.gradle.api.Task;
30-
import org.gradle.api.execution.TaskExecutionGraph;
3130
import org.gradle.api.plugins.BasePlugin;
3231

3332
import com.diffplug.common.base.Errors;
3433
import com.diffplug.spotless.LineEnding;
3534

36-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
37-
import groovy.lang.Closure;
38-
3935
public class SpotlessExtension {
4036
final Project project;
4137
final Task rootCheckTask, rootApplyTask, rootDiagnoseTask;
@@ -237,7 +233,7 @@ private <T extends FormatExtension> T maybeCreate(String name, Class<T> clazz) {
237233
Constructor<T> constructor = clazz.getConstructor(SpotlessExtension.class);
238234
T formatExtension = constructor.newInstance(this);
239235
formats.put(name, formatExtension);
240-
createFormatTask(name, formatExtension);
236+
createFormatTasks(name, formatExtension);
241237
return formatExtension;
242238
} catch (NoSuchMethodException e) {
243239
throw new GradleException("Must have a constructor " + clazz.getSimpleName() + "(SpotlessExtension root)", e);
@@ -247,8 +243,14 @@ private <T extends FormatExtension> T maybeCreate(String name, Class<T> clazz) {
247243
}
248244
}
249245

246+
/**
247+
* Creates 3 tasks for the supplied format:
248+
* - "spotless{FormatName}" is the main `SpotlessTask` that does the work for this format
249+
* - "spotless{FormatName}Check" will depend on the main spotless task in `check` mode
250+
* - "spotless{FormatName}Apply" will depend on the main spotless task in `apply` mode
251+
*/
250252
@SuppressWarnings("rawtypes")
251-
private void createFormatTask(String name, FormatExtension formatExtension) {
253+
private void createFormatTasks(String name, FormatExtension formatExtension) {
252254
// create the SpotlessTask
253255
String taskName = EXTENSION + SpotlessPlugin.capitalize(name);
254256
SpotlessTask spotlessTask = project.getTasks().create(taskName, SpotlessTask.class);
@@ -259,27 +261,15 @@ private void createFormatTask(String name, FormatExtension formatExtension) {
259261
spotlessTask.mustRunAfter(clean);
260262

261263
// create the check and apply control tasks
262-
Task checkTask = project.getTasks().create(taskName + CHECK);
263-
Task applyTask = project.getTasks().create(taskName + APPLY);
264-
264+
SpotlessCheck checkTask = project.getTasks().create(taskName + CHECK, SpotlessCheck.class);
265+
checkTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory());
266+
checkTask.source = spotlessTask;
265267
checkTask.dependsOn(spotlessTask);
268+
269+
SpotlessApply applyTask = project.getTasks().create(taskName + APPLY, SpotlessApply.class);
270+
applyTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory());
271+
applyTask.source = spotlessTask;
266272
applyTask.dependsOn(spotlessTask);
267-
// when the task graph is ready, we'll configure the spotlessTask appropriately
268-
project.getGradle().getTaskGraph().whenReady(new Closure(null) {
269-
private static final long serialVersionUID = 1L;
270-
271-
// called by gradle
272-
@SuppressFBWarnings("UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS")
273-
public Object doCall(TaskExecutionGraph graph) {
274-
if (graph.hasTask(checkTask)) {
275-
spotlessTask.setCheck();
276-
}
277-
if (graph.hasTask(applyTask)) {
278-
spotlessTask.setApply();
279-
}
280-
return Closure.DONE;
281-
}
282-
});
283273

284274
// set the filePatterns property
285275
project.afterEvaluate(unused -> {

0 commit comments

Comments
 (0)