Skip to content

Separate the core SpotlessTask from check and apply #576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 14, 2020
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
10 changes: 7 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ anchors:
GRADLE_OPTS: "-Dorg.gradle.workers.max=2" # and we're only allowed to use 2 vCPUs
docker:
- image: cimg/openjdk:8.0
env_gradle_large: &env_gradle_large
<< : *env_gradle
resource_class: large # https://circleci.com/docs/2.0/configuration-reference/#resource_class
GRADLE_OPTS: "-Dorg.gradle.workers.max=4"

restore_cache_wrapper: &restore_cache_wrapper
restore_cache:
Expand Down Expand Up @@ -41,7 +45,7 @@ version: 2
jobs:
# gradlew spotlessCheck assemble testClasses
assemble_testClasses:
<<: *env_gradle
<<: *env_gradle_large
steps:
- checkout
- *restore_cache_wrapper
Expand All @@ -64,10 +68,10 @@ jobs:
key: gradle-deps2-{{ checksum "build.gradle" }}-{{ checksum "gradle.properties" }}
# JRE8: export SPOTLESS_EXCLUDE_MAVEN=true && ./gradlew check
test_nomaven_8:
<<: *env_gradle
<<: *env_gradle_large
<<: *test_nomaven
test_nomaven_11:
<<: *env_gradle
<<: *env_gradle_large
docker:
- image: cimg/openjdk:11.0
environment: # java 11 does play nice with containers, doesn't need special settings
Expand Down
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]
### Fixed
* `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)).

## [1.30.0] - 2020-05-11
### Added
Expand Down
4 changes: 3 additions & 1 deletion gradle/java-setup.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ spotbugs {
toolVersion = VER_SPOTBUGS
ignoreFailures = false // bug free or it doesn't ship!
reportsDir = file('build/spotbugs')
effort = 'max' // min|default|max
reportLevel = 'medium' // low|medium|high (low = sensitive to even minor mistakes)
omitVisitors = [] // bugs that we want to ignore
}
Expand All @@ -46,6 +45,9 @@ tasks.named('spotbugsTest') {
tasks.named('spotbugsMain') {
// only run on Java 8 (no benefit to running twice)
enabled = org.gradle.api.JavaVersion.current() == org.gradle.api.JavaVersion.VERSION_1_8
reports {
html.enabled = true
}
}
dependencies {
compileOnly 'net.jcip:jcip-annotations:1.0'
Expand Down
3 changes: 1 addition & 2 deletions lib/src/main/java/com/diffplug/spotless/PaddedCell.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -258,7 +257,7 @@ private byte[] canonicalBytes() {
}

public void writeCanonicalTo(File file) throws IOException {
Files.write(file.toPath(), canonicalBytes(), StandardOpenOption.TRUNCATE_EXISTING);
Files.write(file.toPath(), canonicalBytes());
}

public void writeCanonicalTo(OutputStream out) throws IOException {
Expand Down
10 changes: 10 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).

## [Unreleased]
**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.

### Added
* Support for the gradle build cache. ([#576](https://github.com/diffplug/spotless/pull/576))
* The local cache will work great, but the remote cache will always miss until [#566](https://github.com/diffplug/spotless/issues/566) is resolved.
### Removed
* (Power users only) `void SpotlessTask::setCheck()` and `setApply()` have been removed. ([#576](https://github.com/diffplug/spotless/pull/576))
* 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.
### Changed
* (Power users only) `SpotlessTask FormatExtension::createIndependentTask` has been removed, and replaced with `SpotlessApply::createIndependentApplyTask`. ([#576](https://github.com/diffplug/spotless/pull/576))

## [3.30.0] - 2020-05-11
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,30 +613,31 @@ protected Project getProject() {
}

/**
* Creates an independent {@link SpotlessTask} for (very) unusual circumstances.
* Creates an independent {@link SpotlessApply} for (very) unusual circumstances.
*
* Most users will not want this method. In the rare case that you want to create
* a SpotlessTask which is independent of the normal Spotless machinery, this will
* a `SpotlessApply` which is independent of the normal Spotless machinery, this will
* let you do that.
*
* The returned task will have no dependencies on any other task.
* You need to call {@link SpotlessTask#setApply()} and/or {@link SpotlessTask#setCheck()}
* on the return value, otherwise you will get a runtime error when the task tries to run.
* The returned task will not be hooked up to the global `spotlessApply`, and there will be no corresponding `check` task.
*
* 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).
*/
public SpotlessTask createIndependentTask(String taskName) {
public SpotlessApply createIndependentApplyTask(String taskName) {
// create and setup the task
SpotlessTask spotlessTask = root.project.getTasks().create(taskName, SpotlessTask.class);
SpotlessTask spotlessTask = root.project.getTasks().create(taskName + "Helper", SpotlessTask.class);
setupTask(spotlessTask);

// enforce the clean ordering
Task clean = root.project.getTasks().getByName(BasePlugin.CLEAN_TASK_NAME);
spotlessTask.mustRunAfter(clean);

// ignore the filePatterns
spotlessTask.setFilePatterns("");
// create the apply task
SpotlessApply applyTask = root.project.getTasks().create(taskName, SpotlessApply.class);
applyTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory());
applyTask.source = spotlessTask;
applyTask.dependsOn(spotlessTask);

return spotlessTask;
return applyTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2016 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.gradle.spotless;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;

import org.gradle.api.DefaultTask;
import org.gradle.api.file.ConfigurableFileTree;
import org.gradle.api.file.FileVisitDetails;
import org.gradle.api.file.FileVisitor;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;

public class SpotlessApply extends DefaultTask {
SpotlessTask source;
private File spotlessOutDirectory;

@PathSensitive(PathSensitivity.RELATIVE)
@InputDirectory
public File getSpotlessOutDirectory() {
return spotlessOutDirectory;
}

public void setSpotlessOutDirectory(File spotlessOutDirectory) {
this.spotlessOutDirectory = spotlessOutDirectory;
}

@TaskAction
public void performAction() {
ConfigurableFileTree files = getProject().fileTree(spotlessOutDirectory);
if (files.isEmpty()) {
getState().setDidWork(source.getDidWork());
} else {
files.visit(new FileVisitor() {
@Override
public void visitDir(FileVisitDetails fileVisitDetails) {

}

@Override
public void visitFile(FileVisitDetails fileVisitDetails) {
String path = fileVisitDetails.getPath();
File originalSource = new File(getProject().getProjectDir(), path);
try {
getLogger().debug("Copying " + fileVisitDetails.getFile() + " to " + originalSource);
Files.copy(fileVisitDetails.getFile().toPath(), originalSource.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2016 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.gradle.spotless;

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

import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.file.ConfigurableFileTree;
import org.gradle.api.file.FileVisitDetails;
import org.gradle.api.file.FileVisitor;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;

import com.diffplug.spotless.Formatter;
import com.diffplug.spotless.extra.integration.DiffMessageFormatter;

public class SpotlessCheck extends DefaultTask {
SpotlessTask source;
private File spotlessOutDirectory;

@PathSensitive(PathSensitivity.RELATIVE)
@InputDirectory
public File getSpotlessOutDirectory() {
return spotlessOutDirectory;
}

public void setSpotlessOutDirectory(File spotlessOutDirectory) {
this.spotlessOutDirectory = spotlessOutDirectory;
}

@TaskAction
public void performAction() throws Exception {
ConfigurableFileTree files = getProject().fileTree(spotlessOutDirectory);
if (files.isEmpty()) {
getState().setDidWork(source.getDidWork());
} else {
List<File> problemFiles = new ArrayList<>();
files.visit(new FileVisitor() {
@Override
public void visitDir(FileVisitDetails fileVisitDetails) {

}

@Override
public void visitFile(FileVisitDetails fileVisitDetails) {
String path = fileVisitDetails.getPath();
File originalSource = new File(getProject().getProjectDir(), path);
problemFiles.add(originalSource);
}
});

if (!problemFiles.isEmpty()) {
Formatter formatter = source.buildFormatter();
Collections.sort(problemFiles);
throw formatViolationsFor(formatter, problemFiles);
}
}
}

/** Returns an exception which indicates problem files nicely. */
private static GradleException formatViolationsFor(Formatter formatter, List<File> problemFiles) {
return new GradleException(DiffMessageFormatter.builder()
.runToFix("Run 'gradlew spotlessApply' to fix these violations.")
.formatter(formatter)
.problemFiles(problemFiles)
.getMessage());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,11 @@
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.execution.TaskExecutionGraph;
import org.gradle.api.plugins.BasePlugin;

import com.diffplug.common.base.Errors;
import com.diffplug.spotless.LineEnding;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import groovy.lang.Closure;

public class SpotlessExtension {
final Project project;
final Task rootCheckTask, rootApplyTask, rootDiagnoseTask;
Expand Down Expand Up @@ -237,7 +233,7 @@ private <T extends FormatExtension> T maybeCreate(String name, Class<T> clazz) {
Constructor<T> constructor = clazz.getConstructor(SpotlessExtension.class);
T formatExtension = constructor.newInstance(this);
formats.put(name, formatExtension);
createFormatTask(name, formatExtension);
createFormatTasks(name, formatExtension);
return formatExtension;
} catch (NoSuchMethodException e) {
throw new GradleException("Must have a constructor " + clazz.getSimpleName() + "(SpotlessExtension root)", e);
Expand All @@ -247,8 +243,14 @@ private <T extends FormatExtension> T maybeCreate(String name, Class<T> clazz) {
}
}

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

// create the check and apply control tasks
Task checkTask = project.getTasks().create(taskName + CHECK);
Task applyTask = project.getTasks().create(taskName + APPLY);

SpotlessCheck checkTask = project.getTasks().create(taskName + CHECK, SpotlessCheck.class);
checkTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory());
checkTask.source = spotlessTask;
checkTask.dependsOn(spotlessTask);

SpotlessApply applyTask = project.getTasks().create(taskName + APPLY, SpotlessApply.class);
applyTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory());
applyTask.source = spotlessTask;
applyTask.dependsOn(spotlessTask);
// when the task graph is ready, we'll configure the spotlessTask appropriately
project.getGradle().getTaskGraph().whenReady(new Closure(null) {
private static final long serialVersionUID = 1L;

// called by gradle
@SuppressFBWarnings("UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS")
public Object doCall(TaskExecutionGraph graph) {
if (graph.hasTask(checkTask)) {
spotlessTask.setCheck();
}
if (graph.hasTask(applyTask)) {
spotlessTask.setApply();
}
return Closure.DONE;
}
});

// set the filePatterns property
project.afterEvaluate(unused -> {
Expand Down
Loading