com.diffplug.gradle.spotless.SpotlessExtension Maven / Gradle / Ivy
Show all versions of spotless-plugin-gradle Show documentation
/*
* Copyright 2016-2024 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 java.util.Objects.requireNonNull;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Nullable;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.language.base.plugins.LifecycleBasePlugin;
import com.diffplug.spotless.LineEnding;
public abstract class SpotlessExtension {
final Project project;
private final RegisterDependenciesTask registerDependenciesTask;
protected static final String TASK_GROUP = LifecycleBasePlugin.VERIFICATION_GROUP;
protected static final String CHECK_DESCRIPTION = "Checks that sourcecode satisfies formatting steps.";
protected static final String APPLY_DESCRIPTION = "Applies code formatting steps to sourcecode in-place.";
static final String EXTENSION = "spotless";
static final String EXTENSION_PREDECLARE = "spotlessPredeclare";
static final String CHECK = "Check";
static final String APPLY = "Apply";
static final String DIAGNOSE = "Diagnose";
protected SpotlessExtension(Project project) {
this.project = requireNonNull(project);
this.registerDependenciesTask = findRegisterDepsTask().get();
}
RegisterDependenciesTask getRegisterDependenciesTask() {
return registerDependenciesTask;
}
/** Line endings (if any). */
LineEnding lineEndings = LineEnding.GIT_ATTRIBUTES_FAST_ALLSAME;
public LineEnding getLineEndings() {
return lineEndings;
}
public void setLineEndings(LineEnding lineEndings) {
if (lineEndings == LineEnding.GIT_ATTRIBUTES) {
throw new IllegalArgumentException("GIT_ATTRIBUTES not supported in Gradle, use GIT_ATTRIBUTES_FAST_ALLSAME instead. See https://github.com/diffplug/spotless/issues/1274 for more details.");
}
this.lineEndings = requireNonNull(lineEndings);
}
Charset encoding = StandardCharsets.UTF_8;
/** Returns the encoding to use. */
public Charset getEncoding() {
return encoding;
}
/** Sets encoding to use (defaults to UTF_8). */
public void setEncoding(Charset charset) {
encoding = requireNonNull(charset);
}
/** Sets encoding to use (defaults to UTF_8). */
public void setEncoding(String name) {
requireNonNull(name);
setEncoding(Charset.forName(name));
}
/** Sets encoding to use (defaults to UTF_8). */
public void encoding(Charset charset) {
setEncoding(charset);
}
/** Sets encoding to use (defaults to UTF_8). */
public void encoding(String charset) {
setEncoding(charset);
}
private @Nullable String ratchetFrom;
/**
* Limits the target to only the files which have changed since the given git reference,
* which is resolved according to this
*/
public void setRatchetFrom(String ratchetFrom) {
this.ratchetFrom = ratchetFrom;
}
/** @see #setRatchetFrom(String) */
public @Nullable String getRatchetFrom() {
return ratchetFrom;
}
/** @see #setRatchetFrom(String) */
public void ratchetFrom(String ratchetFrom) {
setRatchetFrom(ratchetFrom);
}
final Map formats = new LinkedHashMap<>();
/** Configures the special java-specific extension. */
public void java(Action closure) {
requireNonNull(closure);
format(JavaExtension.NAME, JavaExtension.class, closure);
}
/** Configures the special scala-specific extension. */
public void scala(Action closure) {
requireNonNull(closure);
format(ScalaExtension.NAME, ScalaExtension.class, closure);
}
/** Configures the special kotlin-specific extension. */
public void kotlin(Action closure) {
requireNonNull(closure);
format(KotlinExtension.NAME, KotlinExtension.class, closure);
}
/** Configures the special Gradle Kotlin DSL specific extension. */
public void kotlinGradle(Action closure) {
requireNonNull(closure);
format(KotlinGradleExtension.NAME, KotlinGradleExtension.class, closure);
}
/** Configures the special freshmark-specific extension. */
public void freshmark(Action closure) {
requireNonNull(closure);
format(FreshMarkExtension.NAME, FreshMarkExtension.class, closure);
}
/** Configures the special flexmark-specific extension. */
public void flexmark(Action closure) {
requireNonNull(closure);
format(FlexmarkExtension.NAME, FlexmarkExtension.class, closure);
}
/** Configures the special groovy-specific extension. */
public void groovy(Action closure) {
format(GroovyExtension.NAME, GroovyExtension.class, closure);
}
/** Configures the special groovy-specific extension for Gradle files. */
public void groovyGradle(Action closure) {
format(GroovyGradleExtension.NAME, GroovyGradleExtension.class, closure);
}
/** Configures the special sql-specific extension for SQL files. */
public void sql(Action closure) {
format(SqlExtension.NAME, SqlExtension.class, closure);
}
/** Configures the special C/C++-specific extension. */
public void cpp(Action closure) {
format(CppExtension.NAME, CppExtension.class, closure);
}
/** Configures the special javascript-specific extension for javascript files. */
public void javascript(Action closure) {
format(JavascriptExtension.NAME, JavascriptExtension.class, closure);
}
/** Configures the special typescript-specific extension for typescript files. */
public void typescript(Action closure) {
format(TypescriptExtension.NAME, TypescriptExtension.class, closure);
}
/** Configures the special antlr4-specific extension for antlr4 files. */
public void antlr4(Action closure) {
format(Antlr4Extension.NAME, Antlr4Extension.class, closure);
}
/** Configures the special python-specific extension for python files. */
public void python(Action closure) {
format(PythonExtension.NAME, PythonExtension.class, closure);
}
/** Configures the special JSON-specific extension. */
public void json(Action closure) {
requireNonNull(closure);
format(JsonExtension.NAME, JsonExtension.class, closure);
}
/** Configures the special protobuf-specific extension. */
public void protobuf(Action closure) {
requireNonNull(closure);
format(ProtobufExtension.NAME, ProtobufExtension.class, closure);
}
/** Configures the special shell-specific extension. */
public void shell(Action closure) {
requireNonNull(closure);
format(ShellExtension.NAME, ShellExtension.class, closure);
}
/** Configures the special YAML-specific extension. */
public void yaml(Action closure) {
requireNonNull(closure);
format(YamlExtension.NAME, YamlExtension.class, closure);
}
/** Configures the special Gherkin-specific extension. */
public void gherkin(Action closure) {
requireNonNull(closure);
format(GherkinExtension.NAME, GherkinExtension.class, closure);
}
public void go(Action closure) {
requireNonNull(closure);
format(GoExtension.NAME, GoExtension.class, closure);
}
/** Configures the special POM-specific extension. */
public void pom(Action closure) {
requireNonNull(closure);
format(PomExtension.NAME, PomExtension.class, closure);
}
/** Configures a custom extension. */
public void format(String name, Action closure) {
requireNonNull(name, "name");
requireNonNull(closure, "closure");
format(name, FormatExtension.class, closure);
}
boolean enforceCheck = true;
/** Returns {@code true} if Gradle's {@code check} task should run {@code spotlessCheck}; {@code false} otherwise. */
public boolean isEnforceCheck() {
return enforceCheck;
}
/**
* Configures Gradle's {@code check} task to run {@code spotlessCheck} if {@code true},
* but to not do so if {@code false}.
*
* {@code true} by default.
*/
public void setEnforceCheck(boolean enforceCheck) {
this.enforceCheck = enforceCheck;
}
@SuppressWarnings("unchecked")
public void format(String name, Class clazz, Action configure) {
maybeCreate(name, clazz).lazyActions.add((Action) configure);
}
@SuppressWarnings("unchecked")
protected final T maybeCreate(String name, Class clazz) {
FormatExtension existing = formats.get(name);
if (existing != null) {
if (!clazz.isInstance(existing)) {
throw new GradleException("Tried to add format named '" + name + "'" +
" of type " + clazz + " but one has already been created of type " + existing.getClass());
} else {
return (T) existing;
}
} else {
T formatExtension = instantiateFormatExtension(clazz);
formats.put(name, formatExtension);
createFormatTasks(name, formatExtension);
return formatExtension;
}
}
T instantiateFormatExtension(Class clazz) {
try {
return project.getObjects().newInstance(clazz, this);
} catch (Exception e) {
throw new GradleException("Must have a constructor " + clazz.getSimpleName() + "(SpotlessExtension root), annotated with @javax.inject.Inject", e);
}
}
protected abstract void createFormatTasks(String name, FormatExtension formatExtension);
TaskProvider findRegisterDepsTask() {
try {
return findRegisterDepsTask(RegisterDependenciesTask.TASK_NAME);
} catch (Exception e) {
// in a composite build there can be multiple Spotless plugins on the classpath, and they will each try to register
// a task on the root project with the same name. That will generate casting errors, which we can catch and try again
// with an identity-specific identifier.
// https://github.com/diffplug/spotless/pull/1001 for details
return findRegisterDepsTask(RegisterDependenciesTask.TASK_NAME + System.identityHashCode(RegisterDependenciesTask.class));
}
}
private TaskProvider findRegisterDepsTask(String taskName) {
TaskContainer rootProjectTasks = project.getRootProject().getTasks();
if (!rootProjectTasks.getNames().contains(taskName)) {
return rootProjectTasks.register(taskName, RegisterDependenciesTask.class, RegisterDependenciesTask::setup);
} else {
return rootProjectTasks.named(taskName, RegisterDependenciesTask.class);
}
}
public void predeclareDepsFromBuildscript() {
if (project.getRootProject() != project) {
throw new GradleException("predeclareDepsFromBuildscript can only be called from the root project");
}
predeclare(GradleProvisioner.Policy.ROOT_BUILDSCRIPT);
}
public void predeclareDeps() {
if (project.getRootProject() != project) {
throw new GradleException("predeclareDeps can only be called from the root project");
}
predeclare(GradleProvisioner.Policy.ROOT_PROJECT);
}
protected void predeclare(GradleProvisioner.Policy policy) {
project.getExtensions().create(SpotlessExtensionPredeclare.class, EXTENSION_PREDECLARE, SpotlessExtensionPredeclare.class, project, policy);
}
}