Compare commits

...

5 Commits

Author SHA1 Message Date
woggioni 85ca793893 removed "setup Gradle" build step
CI / build (push) Successful in 3m56s
2026-02-23 18:55:40 +08:00
woggioni 0ccffb0642 fixed default finalguard exclusion prefix
CI / build (push) Has been cancelled
2026-02-23 18:53:21 +08:00
woggioni 1fe7ce65f6 Excluded generated sources from finalguard plugin
CI / build (push) Successful in 5m58s
Also added optional list of exclusion paths
2026-02-19 22:32:14 +08:00
woggioni c5dcee554e updated finalguard to use plugin arguments instead of system properties 2026-02-18 08:31:09 +08:00
woggioni 91cf489630 updated build to Gradle 9 and JDK 25 2026-02-17 14:54:01 +08:00
21 changed files with 173 additions and 156 deletions
+1 -3
View File
@@ -4,12 +4,10 @@ on:
branches: [ master ] branches: [ master ]
jobs: jobs:
build: build:
runs-on: hostinger runs-on: woryzen
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Execute Gradle build - name: Execute Gradle build
env: env:
PUBLISHER_TOKEN: ${{ secrets.PUBLISHER_TOKEN }} PUBLISHER_TOKEN: ${{ secrets.PUBLISHER_TOKEN }}
+2 -2
View File
@@ -7,7 +7,7 @@ subprojects { subproject ->
java { java {
toolchain { toolchain {
languageVersion = JavaLanguageVersion.of(21) languageVersion = JavaLanguageVersion.of(25)
} }
} }
@@ -49,7 +49,7 @@ subprojects { subproject ->
add("testImplementation", catalog.junit.jupiter.api) add("testImplementation", catalog.junit.jupiter.api)
add("testImplementation", catalog.junit.jupiter.params) add("testImplementation", catalog.junit.jupiter.params)
add("testRuntimeOnly", catalog.junit.jupiter.engine) add("testRuntimeOnly", catalog.junit.jupiter.engine)
// add("testRuntimeOnly", catalog.junit.platform.launcher) add("testRuntimeOnly", catalog.junit.platform.launcher)
add("testImplementation", gradleTestKit()) add("testImplementation", gradleTestKit())
} }
@@ -20,8 +20,10 @@ import org.gradle.api.file.FileCollection;
import org.gradle.api.file.RegularFile; import org.gradle.api.file.RegularFile;
import org.gradle.api.file.RegularFileProperty; import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.model.ObjectFactory; import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.BasePluginExtension;
import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.plugins.ReportingBasePlugin;
import org.gradle.api.provider.Property; import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider; import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Classpath;
@@ -74,8 +76,6 @@ public class ExportDependencies extends DefaultTask {
@Getter(onMethod_ = { @Input }) @Getter(onMethod_ = { @Input })
private final Property<Boolean> showArtifacts; private final Property<Boolean> showArtifacts;
private final JavaPluginConvention javaPluginConvention;
@InputFiles @InputFiles
@Classpath @Classpath
public Provider<FileCollection> getConfigurationFiles() { public Provider<FileCollection> getConfigurationFiles() {
@@ -101,11 +101,10 @@ public class ExportDependencies extends DefaultTask {
@Inject @Inject
public ExportDependencies(ObjectFactory objects) { public ExportDependencies(ObjectFactory objects) {
setGroup(DEPENDENCY_EXPORT_GROUP); setGroup(DEPENDENCY_EXPORT_GROUP);
javaPluginConvention = getProject().getConvention().getPlugin(JavaPluginConvention.class);
configurationName = objects.property(String.class).convention(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); configurationName = objects.property(String.class).convention(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
Provider<File> defaultOutputFileProvider = final JavaPluginExtension javaPluginExtension = getProject().getExtensions().findByType(JavaPluginExtension.class);
getProject().provider(() -> new File(javaPluginConvention.getDocsDir(), "dependencies.dot")); final Provider<RegularFile> defaultOutputFileProvider = javaPluginExtension.getDocsDir().file("dependencies.dot");
outputFile = objects.fileProperty().convention(getProject().getLayout().file(defaultOutputFileProvider)); outputFile = objects.fileProperty().convention(defaultOutputFileProvider);
showArtifacts = objects.property(Boolean.class).convention(false); showArtifacts = objects.property(Boolean.class).convention(false);
} }
@@ -7,7 +7,7 @@ import org.gradle.api.GradleException;
import org.gradle.api.file.RegularFile; import org.gradle.api.file.RegularFile;
import org.gradle.api.file.RegularFileProperty; import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.model.ObjectFactory; import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property; import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider; import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.CacheableTask;
@@ -59,8 +59,6 @@ public class RenderDependencies extends DefaultTask {
return outputFile.map(RegularFile::getAsFile); return outputFile.map(RegularFile::getAsFile);
} }
private final JavaPluginConvention javaPluginConvention;
@Option(option = "output", description = "Set the output file name") @Option(option = "output", description = "Set the output file name")
public void setOutputCli(String outputFile) { public void setOutputCli(String outputFile) {
Provider<File> fileProvider = getProject().provider(() -> new File(outputFile)); Provider<File> fileProvider = getProject().provider(() -> new File(outputFile));
@@ -81,12 +79,11 @@ public class RenderDependencies extends DefaultTask {
public RenderDependencies(ObjectFactory objects) { public RenderDependencies(ObjectFactory objects) {
setGroup(DEPENDENCY_EXPORT_GROUP); setGroup(DEPENDENCY_EXPORT_GROUP);
sourceFile = objects.property(File.class); sourceFile = objects.property(File.class);
javaPluginConvention = getProject().getConvention().getPlugin(JavaPluginConvention.class);
format = objects.property(String.class).convention("xlib"); format = objects.property(String.class).convention("xlib");
graphvizExecutable = objects.property(String.class).convention("dot"); graphvizExecutable = objects.property(String.class).convention("dot");
Provider<File> defaultOutputFileProvider = final JavaPluginExtension javaPluginExtension = getProject().getExtensions().findByType(JavaPluginExtension.class);
getProject().provider(() -> new File(javaPluginConvention.getDocsDir(), "renderedDependencies")); final Provider<RegularFile> defaultOutputFileProvider = javaPluginExtension.getDocsDir().file("renderedDependencies");
outputFile = objects.fileProperty().convention(getProject().getLayout().file(defaultOutputFileProvider) outputFile = objects.fileProperty().convention(defaultOutputFileProvider
.zip(format, (file, type) -> Objects.equals("xlib", type) ? null : file)); .zip(format, (file, type) -> Objects.equals("xlib", type) ? null : file));
getOutputs().upToDateWhen(t -> outputFile.isPresent()); getOutputs().upToDateWhen(t -> outputFile.isPresent());
} }
@@ -4,7 +4,7 @@ plugins {
} }
repositories { repositories {
jcenter() mavenCentral()
mavenLocal() mavenLocal()
} }
@@ -6,7 +6,7 @@ group = "net.woggioni.finalguard"
java { java {
toolchain { toolchain {
languageVersion = JavaLanguageVersion.of(21) languageVersion = JavaLanguageVersion.of(25)
} }
} }
@@ -20,6 +20,19 @@ tasks.named(org.gradle.api.plugins.JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompil
options.compilerArgs << '-parameters' options.compilerArgs << '-parameters'
} }
configurations {
testCompileClasspath {
attributes {
attribute(org.gradle.api.attributes.java.TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25)
}
}
testRuntimeClasspath {
attributes {
attribute(org.gradle.api.attributes.java.TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25)
}
}
}
test { test {
def testCompilationClassPath = sourceSets["main"].output.classesDirs.files + def testCompilationClassPath = sourceSets["main"].output.classesDirs.files +
sourceSets["main"].runtimeClasspath.files + sourceSets["main"].runtimeClasspath.files +
@@ -21,14 +21,21 @@ import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath; import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner; import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees; import com.sun.source.util.Trees;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@@ -36,26 +43,26 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class FinalGuardPlugin implements Plugin { public class FinalGuardPlugin implements Plugin {
public static final String DIAGNOSTIC_LEVEL_KEY = "net.woggioni.finalguard.diagnostic.level"; public static final String DEFAULT_LEVEL_KEY = "default.level";
public static final String IGNORE_ABSTRACT_METHOD_PARAMS_KEY = "net.woggioni.finalguard.ignore.abstract.method.params"; public static final String EXCLUDE_KEY = "exclude";
enum VariableType { enum VariableType {
LOCAL_VAR("net.woggioni.finalguard.diagnostic.local.variable.level"), LOCAL_VAR("local.variable.level"),
METHOD_PARAM("net.woggioni.finalguard.diagnostic.method.param.level"), METHOD_PARAM("method.param.level"),
LOOP_PARAM("net.woggioni.finalguard.diagnostic.for.param.level"), LOOP_PARAM("for.param.level"),
TRY_WITH_PARAM("net.woggioni.finalguard.diagnostic.try.param.level"), TRY_WITH_PARAM("try.param.level"),
CATCH_PARAM("net.woggioni.finalguard.diagnostic.catch.param.level"), CATCH_PARAM("catch.param.level"),
LAMBDA_PARAM("net.woggioni.finalguard.diagnostic.lambda.param.level"), LAMBDA_PARAM("lambda.param.level"),
ABSTRACT_METHOD_PARAM("net.woggioni.finalguard.diagnostic.abstract.method.param.level"); ABSTRACT_METHOD_PARAM("abstract.method.param.level");
private final String propertyKey; private final String argKey;
VariableType(final String propertyKey) { VariableType(final String argKey) {
this.propertyKey = propertyKey; this.argKey = argKey;
} }
public String getPropertyKey() { public String getArgKey() {
return propertyKey; return argKey;
} }
public String getMessage(final String variableName) { public String getMessage(final String variableName) {
@@ -91,12 +98,27 @@ public class FinalGuardPlugin implements Plugin {
private static final class Configuration { private static final class Configuration {
private final Map<VariableType, Diagnostic.Kind> levels; private final Map<VariableType, Diagnostic.Kind> levels;
private final List<String> excludedPaths;
public Configuration() { public Configuration(final String... args) {
final Map<String, String> props = new HashMap<>();
final List<String> excluded = new ArrayList<>();
for (final String arg : args) {
final String[] parts = arg.split("=", 2);
if (parts.length == 2) {
if (EXCLUDE_KEY.equals(parts[0])) {
final Path path = Paths.get(parts[1]);
excluded.add(path.toAbsolutePath().toString());
} else {
props.put(parts[0], parts[1]);
}
}
}
this.excludedPaths = Collections.unmodifiableList(excluded);
final Diagnostic.Kind defaultLevel = final Diagnostic.Kind defaultLevel =
Optional.ofNullable(System.getProperty(DIAGNOSTIC_LEVEL_KEY)).map(Diagnostic.Kind::valueOf).orElse(null); Optional.ofNullable(props.get(DEFAULT_LEVEL_KEY)).map(Diagnostic.Kind::valueOf).orElse(null);
this.levels = Arrays.stream(VariableType.values()).map(vt -> { this.levels = Arrays.stream(VariableType.values()).map(vt -> {
final Diagnostic.Kind level = Optional.ofNullable(System.getProperty(vt.getPropertyKey())).map(Diagnostic.Kind::valueOf).orElse(defaultLevel); final Diagnostic.Kind level = Optional.ofNullable(props.get(vt.getArgKey())).map(Diagnostic.Kind::valueOf).orElse(defaultLevel);
if (level != null) { if (level != null) {
return new AbstractMap.SimpleEntry<>(vt, level); return new AbstractMap.SimpleEntry<>(vt, level);
} else { } else {
@@ -104,13 +126,21 @@ public class FinalGuardPlugin implements Plugin {
} }
}).filter(Objects::nonNull).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); }).filter(Objects::nonNull).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
} }
public boolean isExcluded(final String sourcePath) {
for (final String excludedPath : excludedPaths) {
if (sourcePath.startsWith(excludedPath)) {
return true;
}
}
return false;
}
} }
private static boolean isJava17OrHigher() { private static boolean isJava17OrHigher() {
return System.getProperty("java.version").compareTo("17") >= 0; return System.getProperty("java.version").compareTo("17") >= 0;
} }
private static final Configuration configuration = new Configuration();
private static final boolean isJava17OrHigher = isJava17OrHigher(); private static final boolean isJava17OrHigher = isJava17OrHigher();
@Override @Override
@@ -120,6 +150,7 @@ public class FinalGuardPlugin implements Plugin {
@Override @Override
public void init(JavacTask task, String... args) { public void init(JavacTask task, String... args) {
final Configuration configuration = new Configuration(args);
task.addTaskListener(new TaskListener() { task.addTaskListener(new TaskListener() {
@Override @Override
public void started(TaskEvent e) { public void started(TaskEvent e) {
@@ -128,14 +159,18 @@ public class FinalGuardPlugin implements Plugin {
@Override @Override
public void finished(TaskEvent e) { public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ANALYZE) { if (e.getKind() == TaskEvent.Kind.ANALYZE) {
analyzeFinalVariables(e.getCompilationUnit(), task, e.getTypeElement()); analyzeFinalVariables(e.getCompilationUnit(), task, e.getTypeElement(), configuration);
} }
} }
}); });
} }
private void analyzeFinalVariables(CompilationUnitTree compilationUnit, JavacTask task, Element typeElement) { private void analyzeFinalVariables(CompilationUnitTree compilationUnit, JavacTask task, Element typeElement, Configuration configuration) {
FinalVariableAnalyzer analyzer = new FinalVariableAnalyzer(compilationUnit, task); final String sourcePath = compilationUnit.getSourceFile().toUri().getPath();
if (sourcePath != null && configuration.isExcluded(sourcePath)) {
return;
}
FinalVariableAnalyzer analyzer = new FinalVariableAnalyzer(compilationUnit, task, configuration);
TreePath path = Trees.instance(task).getPath(typeElement); TreePath path = Trees.instance(task).getPath(typeElement);
if (path != null) { if (path != null) {
analyzer.scan(path, null); analyzer.scan(path, null);
@@ -143,12 +178,14 @@ public class FinalGuardPlugin implements Plugin {
} }
private static class FinalVariableAnalyzer extends TreePathScanner<Void, Void> { private static class FinalVariableAnalyzer extends TreePathScanner<Void, Void> {
private final Configuration configuration;
private final CompilationUnitTree compilationUnit; private final CompilationUnitTree compilationUnit;
private final Trees trees; private final Trees trees;
private final Map<String, VariableInfo> variableInfoMap = new LinkedHashMap<>(); private final Map<String, VariableInfo> variableInfoMap = new LinkedHashMap<>();
private final Set<String> reassignedVariables = new HashSet<>(); private final Set<String> reassignedVariables = new HashSet<>();
public FinalVariableAnalyzer(CompilationUnitTree compilationUnit, JavacTask task) { public FinalVariableAnalyzer(CompilationUnitTree compilationUnit, JavacTask task, Configuration configuration) {
this.configuration = configuration;
this.compilationUnit = compilationUnit; this.compilationUnit = compilationUnit;
this.trees = Trees.instance(task); this.trees = Trees.instance(task);
} }
@@ -1,45 +1,23 @@
package net.woggioni.finalguard; package net.woggioni.finalguard;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import javax.tools.Diagnostic; import javax.tools.*;
import javax.tools.FileObject; import java.io.*;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.ABSTRACT_METHOD_PARAM; import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.*;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.CATCH_PARAM;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.LAMBDA_PARAM;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.LOCAL_VAR;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.LOOP_PARAM;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.METHOD_PARAM;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.TRY_WITH_PARAM;
public class PluginTest { public class PluginTest {
@@ -104,18 +82,25 @@ public class PluginTest {
} }
private Optional<Iterable<Diagnostic<? extends JavaFileObject>>> compile(Iterable<URI> sources) { private Optional<Iterable<Diagnostic<? extends JavaFileObject>>> compile(Iterable<URI> sources) {
return compile(sources, "");
}
private Optional<Iterable<Diagnostic<? extends JavaFileObject>>> compile(Iterable<URI> sources, String extraPluginArgs) {
StringWriter output = new StringWriter(); StringWriter output = new StringWriter();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
FileManager fileManager = FileManager fileManager =
new FileManager(compiler.getStandardFileManager(null, null, null)); new FileManager(compiler.getStandardFileManager(null, null, null));
List<JavaFileObject> compilationUnits = StreamSupport.stream(sources.spliterator(), false) List<JavaFileObject> compilationUnits = StreamSupport.stream(sources.spliterator(), false)
.map(SourceFile::new).collect(Collectors.toList()); .map(SourceFile::new).collect(Collectors.toList());
String pluginArg = "-Xplugin:" + FinalGuardPlugin.class.getName() + " " + FinalGuardPlugin.DEFAULT_LEVEL_KEY + "=ERROR";
if (!extraPluginArgs.isEmpty()) {
pluginArg += " " + extraPluginArgs;
}
List<String> arguments = Arrays.asList( List<String> arguments = Arrays.asList(
"-classpath", System.getProperty("test.compilation.classpath"), "-classpath", System.getProperty("test.compilation.classpath"),
"-Xplugin:" + FinalGuardPlugin.class.getName() pluginArg
); );
final ArrayList<Diagnostic<? extends JavaFileObject>> compilerMessages = new ArrayList<>(); final ArrayList<Diagnostic<? extends JavaFileObject>> compilerMessages = new ArrayList<>();
System.setProperty(FinalGuardPlugin.DIAGNOSTIC_LEVEL_KEY, "ERROR");
JavaCompiler.CompilationTask task = compiler.getTask( JavaCompiler.CompilationTask task = compiler.getTask(
output, output,
fileManager, fileManager,
@@ -208,4 +193,25 @@ public class PluginTest {
Assertions.assertTrue(compilationErrors.isEmpty(), "Unexpected compilation errors found in the output"); Assertions.assertTrue(compilationErrors.isEmpty(), "Unexpected compilation errors found in the output");
} }
} }
@Test
public void testExcludedSourceIsSkipped() throws Exception {
// TestCase3 normally produces an error (local var 'n' not final).
// When we exclude the directory containing TestCase3, it should compile without errors.
ClassLoader cl = getClass().getClassLoader();
URI sourceUri = cl.getResource("net/woggioni/finalguard/test/TestCase3.java").toURI();
String sourcePath = new File(sourceUri).getParent();
// Compile WITH exclude — should succeed (no errors)
Optional<Iterable<Diagnostic<? extends JavaFileObject>>> resultWithExclude =
compile(Collections.singletonList(sourceUri), FinalGuardPlugin.EXCLUDE_KEY + "=" + sourcePath);
Assertions.assertFalse(resultWithExclude.isPresent(),
"Compilation should succeed when source directory is excluded");
// Compile WITHOUT exclude — should fail (TestCase3 has errors)
Optional<Iterable<Diagnostic<? extends JavaFileObject>>> resultWithoutExclude =
compile(Collections.singletonList(sourceUri));
Assertions.assertTrue(resultWithoutExclude.isPresent(),
"Compilation should fail when source directory is NOT excluded");
}
} }
@@ -1,4 +1,2 @@
import java.util.Arrays;
public record TestCase12<T, U>(double x, double y) { public record TestCase12<T, U>(double x, double y) {
} }
@@ -1,5 +1,3 @@
import java.util.Arrays;
public interface TestCase13 { public interface TestCase13 {
void foo(double x, double y); void foo(double x, double y);
} }
@@ -1,5 +1,3 @@
import java.util.Arrays;
public abstract class TestCase14 { public abstract class TestCase14 {
abstract public void foo(double x, double y); abstract public void foo(double x, double y);
} }
@@ -1,5 +1,6 @@
package net.woggioni.gradle.finalguard; package net.woggioni.gradle.finalguard;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property; import org.gradle.api.provider.Property;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
@@ -21,4 +22,8 @@ public interface FinalGuardExtension {
Property<Diagnostic.Kind> getAbstractMethodParameterLevel(); Property<Diagnostic.Kind> getAbstractMethodParameterLevel();
Property<Diagnostic.Kind> getCatchParameterLevel(); Property<Diagnostic.Kind> getCatchParameterLevel();
Property<Boolean> getSkipGeneratedSources();
ListProperty<String> getExcludedPrefixes();
} }
@@ -14,14 +14,15 @@ import org.gradle.api.tasks.compile.CompileOptions;
import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.compile.JavaCompile;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import java.io.File;
import java.net.URL; import java.net.URL;
import java.util.function.BiConsumer;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.Manifest; import java.util.jar.Manifest;
public class FinalGuardPlugin implements Plugin<Project> { public class FinalGuardPlugin implements Plugin<Project> {
private static final String FINALGUARD_PLUGIN_CONFIGURATION = "finalguard_plugin"; private static final String FINALGUARD_PLUGIN_CONFIGURATION = "finalguard_plugin";
private static final String PROPERTY_PREFIX = "net.woggioni.finalguard.diagnostic."; private static final String JAVAC_PLUGIN_NAME = "net.woggioni.finalguard.FinalGuardPlugin";
private static final String EXCLUDE_KEY = "exclude";
@Override @Override
public void apply(final Project project) { public void apply(final Project project) {
@@ -50,74 +51,42 @@ public class FinalGuardPlugin implements Plugin<Project> {
}); });
final FinalGuardExtension finalGuardExtension = objects.newInstance(FinalGuardExtension.class); final FinalGuardExtension finalGuardExtension = objects.newInstance(FinalGuardExtension.class);
extensionContainer.add("finalGuard", finalGuardExtension); finalGuardExtension.getSkipGeneratedSources().convention(true);
extensionContainer.add("finalguard", finalGuardExtension);
tasks.withType(JavaCompile.class, javaCompileTask -> { tasks.withType(JavaCompile.class, javaCompileTask -> {
javaCompileTask.doFirst(t -> { javaCompileTask.doFirst(t -> {
final CompileOptions options = javaCompileTask.getOptions(); final CompileOptions options = javaCompileTask.getOptions();
final BiConsumer<String, Diagnostic.Kind> setProperty = (key, diagnosticKind) -> {
final String propertyKey = PROPERTY_PREFIX + key;
System.setProperty(propertyKey, diagnosticKind.toString());
options.getForkOptions().getJvmArgs().add(String.format("-D%s=%s", propertyKey, diagnosticKind));
};
options.setAnnotationProcessorPath(options.getAnnotationProcessorPath().plus(javacPluginConfiguration)); options.setAnnotationProcessorPath(options.getAnnotationProcessorPath().plus(javacPluginConfiguration));
{ final StringBuilder xpluginArg = new StringBuilder("-Xplugin:").append(JAVAC_PLUGIN_NAME);
final Property<Diagnostic.Kind> defaultLevel = finalGuardExtension.getDefaultLevel(); appendOption(xpluginArg, "default.level", finalGuardExtension.getDefaultLevel());
if (defaultLevel.isPresent()) { appendOption(xpluginArg, "local.variable.level", finalGuardExtension.getLocalVariableLevel());
final Diagnostic.Kind diagnosticKind = defaultLevel.get(); appendOption(xpluginArg, "method.param.level", finalGuardExtension.getMethodParameterLevel());
setProperty.accept("level", diagnosticKind); appendOption(xpluginArg, "abstract.method.param.level", finalGuardExtension.getAbstractMethodParameterLevel());
appendOption(xpluginArg, "for.param.level", finalGuardExtension.getForLoopParameterLevel());
appendOption(xpluginArg, "try.param.level", finalGuardExtension.getTryWithResourceLevel());
appendOption(xpluginArg, "catch.param.level", finalGuardExtension.getCatchParameterLevel());
appendOption(xpluginArg, "lambda.param.level", finalGuardExtension.getLambdaParameterLevel());
if (finalGuardExtension.getSkipGeneratedSources().getOrElse(true)) {
final File generatedSourceDir = project.getLayout().getBuildDirectory().getAsFile().get();
if (generatedSourceDir != null) {
appendOption(xpluginArg, EXCLUDE_KEY, generatedSourceDir.getAbsolutePath());
} }
} }
{ for(final String excludedPrefix : finalGuardExtension.getExcludedPrefixes().get()) {
final Property<Diagnostic.Kind> localVariableLevel = finalGuardExtension.getLocalVariableLevel(); appendOption(xpluginArg, EXCLUDE_KEY, excludedPrefix);
if (localVariableLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = localVariableLevel.get();
setProperty.accept("local.variable.level", diagnosticKind);
}
} }
{ options.getCompilerArgs().add(xpluginArg.toString());
final Property<Diagnostic.Kind> methodParameterLevel = finalGuardExtension.getMethodParameterLevel();
if (methodParameterLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = methodParameterLevel.get();
setProperty.accept("method.param.level", diagnosticKind);
}
}
{
final Property<Diagnostic.Kind> abstractMethodParameterLevel = finalGuardExtension.getAbstractMethodParameterLevel();
if (abstractMethodParameterLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = abstractMethodParameterLevel.get();
setProperty.accept("abstract.method.param.level", diagnosticKind);
}
}
{
final Property<Diagnostic.Kind> forLoopParameterLevel = finalGuardExtension.getForLoopParameterLevel();
if (forLoopParameterLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = forLoopParameterLevel.get();
setProperty.accept("for.param.level", diagnosticKind);
}
}
{
final Property<Diagnostic.Kind> tryParameterLevel = finalGuardExtension.getTryWithResourceLevel();
if (tryParameterLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = tryParameterLevel.get();
setProperty.accept("try.param.level", diagnosticKind);
}
}
{
final Property<Diagnostic.Kind> catchParameterLevel = finalGuardExtension.getCatchParameterLevel();
if (catchParameterLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = catchParameterLevel.get();
setProperty.accept("catch.param.level", diagnosticKind);
}
}
{
final Property<Diagnostic.Kind> lambdaParameterLevel = finalGuardExtension.getLambdaParameterLevel();
if (lambdaParameterLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = lambdaParameterLevel.get();
setProperty.accept("lambda.param.level", diagnosticKind);
}
}
options.getCompilerArgs().add("-Xplugin:net.woggioni.finalguard.FinalGuardPlugin");
}); });
}); });
} }
private static void appendOption(StringBuilder sb, String key, String value) {
sb.append(' ').append(key).append('=').append(value);
}
private static void appendOption(StringBuilder sb, String key, Property<Diagnostic.Kind> property) {
if (property.isPresent()) {
appendOption(sb, key, property.get().toString());
}
}
} }
+3 -3
View File
@@ -1,5 +1,5 @@
lys.catalog.version=2025.11.18 lys.catalog.version=2025.12.27
version.myGradlePlugins=2025.11.29 version.myGradlePlugins=2026.02.23
version.gradle=8.12 version.gradle=9.3.1
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
Vendored
+3 -6
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -205,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.
Vendored
+1 -2
View File
@@ -70,11 +70,10 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
+6
View File
@@ -2,6 +2,12 @@ plugins {
id 'groovy-gradle-plugin' id 'groovy-gradle-plugin'
} }
java {
sourceCompatibility(JavaVersion.VERSION_1_8.toString())
targetCompatibility(JavaVersion.VERSION_1_8.toString())
modularity.inferModulePath = false
}
gradlePlugin { gradlePlugin {
plugins { plugins {
create("JPMSCheckPlugin") { create("JPMSCheckPlugin") {
@@ -10,7 +10,6 @@ import org.gradle.api.attributes.LibraryElements;
import org.gradle.api.attributes.java.TargetJvmVersion; import org.gradle.api.attributes.java.TargetJvmVersion;
import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileCollection;
import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.internal.plugins.DslObject;
import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.MapProperty;
@@ -167,7 +166,6 @@ public class MultiReleaseJarPlugin implements Plugin<Project> {
"classes/" + mainSourceSet.getName() + "/" + sourceDirectorySet.getName()) "classes/" + mainSourceSet.getName() + "/" + sourceDirectorySet.getName())
); );
sourcePaths.add(sourceDirectorySet.getSourceDirectories()); sourcePaths.add(sourceDirectorySet.getSourceDirectories());
new DslObject(mainSourceSet).getConvention().getPlugins().put(sourceDirectorySet.getName(), sourceDirectorySet);
mainSourceSet.getExtensions().add(SourceDirectorySet.class, sourceDirectorySet.getName(), sourceDirectorySet); mainSourceSet.getExtensions().add(SourceDirectorySet.class, sourceDirectorySet.getName(), sourceDirectorySet);
TaskProvider<JavaCompile> compileTask = TaskProvider<JavaCompile> compileTask =
project.getTasks().register(JavaPlugin.COMPILE_JAVA_TASK_NAME + javaVersion.getMajorVersion(), JavaCompile.class, project.getTasks().register(JavaPlugin.COMPILE_JAVA_TASK_NAME + javaVersion.getMajorVersion(), JavaCompile.class,
-1
View File
@@ -10,7 +10,6 @@ dependencyResolutionManagement {
versionCatalogs { versionCatalogs {
catalog { catalog {
from group: 'com.lys', name: 'lys-catalog', version: getProperty('lys.catalog.version') from group: 'com.lys', name: 'lys-catalog', version: getProperty('lys.catalog.version')
version('junit.jupiter', '5.9.3')
} }
} }
} }