Skip to content

Task configuration avoidance (WIP) (#269) #277

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

Closed
wants to merge 16 commits into from
Closed
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
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ VER_PEGDOWN_DOCLET=1.3
# Used in multiple places
VER_DURIAN=1.2.0
VER_JUNIT=4.12
VER_ASSERTJ=3.5.2
VER_ASSERTJ=3.10.0
VER_MOCKITO=2.13.0

# Used for Maven Plugin
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.File;

import org.assertj.core.api.SoftAssertions;
import org.junit.Test;

import com.diffplug.spotless.FormatterStep;
Expand Down Expand Up @@ -65,25 +66,26 @@ public abstract class EclipseCommonTests extends ResourceHarness {

@Test
public void testSupportedVersions() throws Exception {
String[] versions = getSupportedVersions();
for (String version : versions) {
SoftAssertions softly = new SoftAssertions();
for (String version : getSupportedVersions()) {
String input = getTestInput(version);
String expected = getTestExpectation(version);
File inputFile = setFile("someInputFile").toContent(input);
FormatterStep step = null;
try {
step = createStep(version);
} catch (Exception e) {
fail("Exception occured when instantiating step for version: " + version, e);
fail("Exception occurred when instantiating step for version: " + version, e);
}
String output = null;
try {
output = step.format(input, inputFile);
} catch (Exception e) {
fail("Exception occured when formatting input with version: " + version, e);
fail("Exception occurred when formatting input with version: " + version, e);
}
assertThat(output).as("Formatting output unexpected with version: " + version).isEqualTo(expected);
softly.assertThat(output).as("Formatting output unexpected with version: " + version).isEqualTo(expected);
}
softly.assertAll();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,15 @@
*/
package com.diffplug.gradle.spotless;

import static com.diffplug.gradle.spotless.SpotlessTaskConstants.EXTENSION;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.execution.TaskExecutionGraph;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaBasePlugin;

import com.diffplug.spotless.SpotlessCache;

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

public class SpotlessPlugin implements Plugin<Project> {
SpotlessExtension spotlessExtension;

static final String EXTENSION = "spotless";
static final String CHECK = "Check";
static final String APPLY = "Apply";

private static final String TASK_GROUP = "Verification";
private static final String CHECK_DESCRIPTION = "Checks that sourcecode satisfies formatting steps.";
private static final String APPLY_DESCRIPTION = "Applies code formatting steps to sourcecode in-place.";

@Override
public void apply(Project project) {
// make sure there's a `clean` task
Expand All @@ -55,67 +41,7 @@ public SpotlessExtension getExtension() {
return spotlessExtension;
}

@SuppressWarnings("rawtypes")
void createTasks(Project project) {
Task rootCheckTask = project.task(EXTENSION + CHECK);
rootCheckTask.setGroup(TASK_GROUP);
rootCheckTask.setDescription(CHECK_DESCRIPTION);
Task rootApplyTask = project.task(EXTENSION + APPLY);
rootApplyTask.setGroup(TASK_GROUP);
rootApplyTask.setDescription(APPLY_DESCRIPTION);

spotlessExtension.formats.forEach((key, value) -> {
// create the task that does the work
String taskName = EXTENSION + capitalize(key);
SpotlessTask spotlessTask = project.getTasks().create(taskName, SpotlessTask.class);
value.setupTask(spotlessTask);

// create the check and apply control tasks
Task checkTask = project.getTasks().create(taskName + CHECK);
Task applyTask = project.getTasks().create(taskName + APPLY);
// the root tasks depend on them
rootCheckTask.dependsOn(checkTask);
rootApplyTask.dependsOn(applyTask);
// and they depend on the work task
checkTask.dependsOn(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;
}
});
});

// Add our check task as a dependency on the global check task
// getTasks() returns a "live" collection, so this works even if the
// task doesn't exist at the time this call is made
if (spotlessExtension.enforceCheck) {
project.getTasks()
.matching(task -> task.getName().equals(JavaBasePlugin.CHECK_TASK_NAME))
.all(task -> task.dependsOn(rootCheckTask));
}

// clear spotless' cache when the user does a clean, but only after any spotless tasks
Task clean = project.getTasks().getByName(BasePlugin.CLEAN_TASK_NAME);
clean.doLast(unused -> SpotlessCache.clear());
project.getTasks()
.withType(SpotlessTask.class)
.all(task -> task.mustRunAfter(clean));
}

static String capitalize(String input) {
return Character.toUpperCase(input.charAt(0)) + input.substring(1);
SpotlessTaskSetup.detectAppropriateImplementation().accept(project, spotlessExtension);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ public void setApply() {
/** Returns the name of this format. */
String formatName() {
String name = getName();
if (name.startsWith(SpotlessPlugin.EXTENSION)) {
return name.substring(SpotlessPlugin.EXTENSION.length()).toLowerCase(Locale.ROOT);
if (name.startsWith(SpotlessTaskConstants.EXTENSION)) {
return name.substring(SpotlessTaskConstants.EXTENSION.length()).toLowerCase(Locale.ROOT);
} else {
return name;
}
Expand All @@ -157,7 +157,7 @@ public void performAction(IncrementalTaskInputs inputs) throws Exception {
throw new GradleException("You must specify 'Iterable<File> toFormat'");
}
if (!check && !apply) {
throw new GradleException("Don't call " + getName() + " directly, call " + getName() + SpotlessPlugin.CHECK + " or " + getName() + SpotlessPlugin.APPLY);
throw new GradleException("Don't call " + getName() + " directly, call " + getName() + SpotlessTaskConstants.CHECK + " or " + getName() + SpotlessTaskConstants.APPLY);
}

// create the formatter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.util.Objects;

final class SpotlessTaskConstants {
static final String EXTENSION = "spotless";
static final String CHECK = "Check";
static final String APPLY = "Apply";

static final String TASK_GROUP = "Verification";
static final String CHECK_DESCRIPTION = "Checks that sourcecode satisfies formatting steps.";
static final String APPLY_DESCRIPTION = "Applies code formatting steps to sourcecode in-place.";

static String capitalize(String input) {
Objects.requireNonNull(input, "input");
return Character.toUpperCase(input.charAt(0)) + input.substring(1);
}

// prevent instantiation
private SpotlessTaskConstants() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.util.function.BiConsumer;

import org.gradle.api.Project;

interface SpotlessTaskSetup extends BiConsumer<Project, SpotlessExtension> {
static SpotlessTaskSetup detectAppropriateImplementation() {
try {
Class.forName("org.gradle.api.tasks.TaskProvider");
// Instantiate SpotlessTaskSetupConfigAvoidance by its fully qualified name, so that
// classloaders don't attempt to load it when Spotless is running on a relatively
// young version of Gradle where org.gradle.api.tasks.TaskProvider doesn't exist.
return new com.diffplug.gradle.spotless.SpotlessTaskSetupConfigAvoidance();
} catch (ClassNotFoundException e) {
return new com.diffplug.gradle.spotless.SpotlessTaskSetupLegacy();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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 static com.diffplug.gradle.spotless.SpotlessTaskConstants.APPLY;
import static com.diffplug.gradle.spotless.SpotlessTaskConstants.APPLY_DESCRIPTION;
import static com.diffplug.gradle.spotless.SpotlessTaskConstants.CHECK;
import static com.diffplug.gradle.spotless.SpotlessTaskConstants.CHECK_DESCRIPTION;
import static com.diffplug.gradle.spotless.SpotlessTaskConstants.EXTENSION;
import static com.diffplug.gradle.spotless.SpotlessTaskConstants.TASK_GROUP;
import static com.diffplug.gradle.spotless.SpotlessTaskConstants.capitalize;

import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.TaskProvider;

import com.diffplug.spotless.SpotlessCache;

final class SpotlessTaskSetupConfigAvoidance implements SpotlessTaskSetup {
@Override
public void accept(Project project, SpotlessExtension spotlessExtension) {
TaskProvider<Task> rootCheckTaskProvider = project.getTasks().register(
EXTENSION + CHECK,
task -> {
task.setGroup(TASK_GROUP);
task.setDescription(CHECK_DESCRIPTION);
});
TaskProvider<Task> rootApplyTaskProvider = project.getTasks().register(
EXTENSION + APPLY,
task -> {
task.setGroup(TASK_GROUP);
task.setDescription(APPLY_DESCRIPTION);
});

spotlessExtension.formats.forEach((key, value) -> {
// create the task that does the work
String taskName = EXTENSION + capitalize(key);
TaskProvider<SpotlessTask> spotlessTaskProvider = project.getTasks().register(taskName, SpotlessTask.class, value::setupTask);

// create the check and apply control tasks
TaskProvider<Task> checkTaskProvider = project.getTasks().register(taskName + CHECK);
TaskProvider<Task> applyTaskProvider = project.getTasks().register(taskName + APPLY);
// the root tasks depend on them
rootCheckTaskProvider.configure(rootCheckTask -> rootCheckTask.dependsOn(checkTaskProvider));
rootApplyTaskProvider.configure(rootApplyTask -> rootApplyTask.dependsOn(applyTaskProvider));
// and they depend on the work task
checkTaskProvider.configure(checkTask -> checkTask.dependsOn(spotlessTaskProvider));
applyTaskProvider.configure(applyTask -> applyTask.dependsOn(spotlessTaskProvider));

// when the task graph is ready, we'll configure the spotlessTask appropriately
project.getGradle().getTaskGraph().whenReady(graph -> {
if (checkTaskProvider.isPresent() && graph.hasTask(checkTaskProvider.get())) {
spotlessTaskProvider.configure(SpotlessTask::setCheck);
}
if (applyTaskProvider.isPresent() && graph.hasTask(applyTaskProvider.get())) {
spotlessTaskProvider.configure(SpotlessTask::setApply);
}
});
});

// Add our check task as a dependency on the global check task.
// getTasks() returns a "live" collection and configureEach() is lazy,
// so this works even if the task doesn't exist at the time this call
// is made.
if (spotlessExtension.enforceCheck) {
project.getTasks()
.matching(task -> task.getName().equals(JavaBasePlugin.CHECK_TASK_NAME))
.configureEach(task -> task.dependsOn(rootCheckTaskProvider));
}

// clear spotless' cache when the user does a clean, but only after any spotless tasks
TaskProvider<Task> cleanTaskProvider = project.getTasks().named(BasePlugin.CLEAN_TASK_NAME);
cleanTaskProvider.configure(task -> task.doLast(unused -> SpotlessCache.clear()));
project.getTasks()
.withType(SpotlessTask.class)
.configureEach(task -> task.mustRunAfter(cleanTaskProvider));
}
}
Loading