diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 5d29c8c..f0ff2a3 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -4,7 +4,7 @@ on: branches: [ master ] jobs: build: - runs-on: hostinger + runs-on: woryzen steps: - name: Checkout sources uses: actions/checkout@v4 diff --git a/finalguard/finalguard-javac-plugin/src/main/java/net/woggioni/finalguard/FinalGuardPlugin.java b/finalguard/finalguard-javac-plugin/src/main/java/net/woggioni/finalguard/FinalGuardPlugin.java index 0629205..2f5fbea 100644 --- a/finalguard/finalguard-javac-plugin/src/main/java/net/woggioni/finalguard/FinalGuardPlugin.java +++ b/finalguard/finalguard-javac-plugin/src/main/java/net/woggioni/finalguard/FinalGuardPlugin.java @@ -1,35 +1,18 @@ package net.woggioni.finalguard; -import com.sun.source.tree.AssignmentTree; -import com.sun.source.tree.BlockTree; -import com.sun.source.tree.CatchTree; -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.CompoundAssignmentTree; -import com.sun.source.tree.EnhancedForLoopTree; -import com.sun.source.tree.ForLoopTree; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.LambdaExpressionTree; -import com.sun.source.tree.MethodTree; -import com.sun.source.tree.Tree; -import com.sun.source.tree.TryTree; -import com.sun.source.tree.UnaryTree; -import com.sun.source.tree.VariableTree; -import com.sun.source.util.JavacTask; -import com.sun.source.util.Plugin; -import com.sun.source.util.TaskEvent; -import com.sun.source.util.TaskListener; -import com.sun.source.util.TreePath; -import com.sun.source.util.TreePathScanner; -import com.sun.source.util.Trees; +import com.sun.source.tree.*; +import com.sun.source.util.*; + import javax.lang.model.element.Element; -import javax.lang.model.element.Modifier; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; import javax.tools.Diagnostic; import java.util.*; import java.util.stream.Collectors; public class FinalGuardPlugin implements Plugin { public static final String DEFAULT_LEVEL_KEY = "default.level"; + public static final String EXCLUDE_KEY = "exclude"; public static final String IGNORE_ABSTRACT_METHOD_PARAMS_KEY = "net.woggioni.finalguard.ignore.abstract.method.params"; enum VariableType { LOCAL_VAR("local.variable.level"), @@ -83,15 +66,22 @@ public class FinalGuardPlugin implements Plugin { private static final class Configuration { private final Map levels; + private final List excludedPaths; public Configuration(final String... args) { final Map props = new HashMap<>(); + final List excluded = new ArrayList<>(); for (final String arg : args) { final String[] parts = arg.split("=", 2); if (parts.length == 2) { - props.put(parts[0], parts[1]); + if (EXCLUDE_KEY.equals(parts[0])) { + excluded.add(parts[1]); + } else { + props.put(parts[0], parts[1]); + } } } + this.excludedPaths = Collections.unmodifiableList(excluded); final Diagnostic.Kind defaultLevel = Optional.ofNullable(props.get(DEFAULT_LEVEL_KEY)).map(Diagnostic.Kind::valueOf).orElse(null); this.levels = Arrays.stream(VariableType.values()).map(vt -> { @@ -103,6 +93,15 @@ public class FinalGuardPlugin implements Plugin { } }).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() { @@ -134,6 +133,10 @@ public class FinalGuardPlugin implements Plugin { } private void analyzeFinalVariables(CompilationUnitTree compilationUnit, JavacTask task, Element typeElement, Configuration configuration) { + 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); if (path != null) { diff --git a/finalguard/finalguard-javac-plugin/src/test/java/net/woggioni/finalguard/PluginTest.java b/finalguard/finalguard-javac-plugin/src/test/java/net/woggioni/finalguard/PluginTest.java index 004cae1..2d710aa 100644 --- a/finalguard/finalguard-javac-plugin/src/test/java/net/woggioni/finalguard/PluginTest.java +++ b/finalguard/finalguard-javac-plugin/src/test/java/net/woggioni/finalguard/PluginTest.java @@ -1,6 +1,7 @@ package net.woggioni.finalguard; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -81,15 +82,23 @@ public class PluginTest { } private Optional>> compile(Iterable sources) { + return compile(sources, ""); + } + + private Optional>> compile(Iterable sources, String extraPluginArgs) { StringWriter output = new StringWriter(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); FileManager fileManager = new FileManager(compiler.getStandardFileManager(null, null, null)); List compilationUnits = StreamSupport.stream(sources.spliterator(), false) .map(SourceFile::new).collect(Collectors.toList()); + String pluginArg = "-Xplugin:" + FinalGuardPlugin.class.getName() + " " + FinalGuardPlugin.DEFAULT_LEVEL_KEY + "=ERROR"; + if (!extraPluginArgs.isEmpty()) { + pluginArg += " " + extraPluginArgs; + } List arguments = Arrays.asList( "-classpath", System.getProperty("test.compilation.classpath"), - "-Xplugin:" + FinalGuardPlugin.class.getName() + " " + FinalGuardPlugin.DEFAULT_LEVEL_KEY + "=ERROR" + pluginArg ); final ArrayList> compilerMessages = new ArrayList<>(); JavaCompiler.CompilationTask task = compiler.getTask( @@ -184,4 +193,25 @@ public class PluginTest { 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>> 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>> resultWithoutExclude = + compile(Collections.singletonList(sourceUri)); + Assertions.assertTrue(resultWithoutExclude.isPresent(), + "Compilation should fail when source directory is NOT excluded"); + } } diff --git a/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase12.java b/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase12.java index 11561bb..eeb2099 100644 --- a/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase12.java +++ b/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase12.java @@ -1,4 +1,2 @@ -import java.util.Arrays; - public record TestCase12(double x, double y) { } \ No newline at end of file diff --git a/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase13.java b/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase13.java index fb4b820..f1d9223 100644 --- a/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase13.java +++ b/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase13.java @@ -1,5 +1,3 @@ -import java.util.Arrays; - public interface TestCase13 { void foo(double x, double y); } \ No newline at end of file diff --git a/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase14.java b/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase14.java index 4aa88bd..17789cb 100644 --- a/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase14.java +++ b/finalguard/finalguard-javac-plugin/src/test/resources/net/woggioni/finalguard/test/TestCase14.java @@ -1,5 +1,3 @@ -import java.util.Arrays; - public abstract class TestCase14 { abstract public void foo(double x, double y); } \ No newline at end of file diff --git a/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardExtension.java b/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardExtension.java index 380de64..b0fef82 100644 --- a/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardExtension.java +++ b/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardExtension.java @@ -1,5 +1,6 @@ package net.woggioni.gradle.finalguard; +import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import javax.tools.Diagnostic; @@ -21,4 +22,8 @@ public interface FinalGuardExtension { Property getAbstractMethodParameterLevel(); Property getCatchParameterLevel(); + + Property getSkipGeneratedSources(); + + ListProperty getExcludedPrefixes(); } diff --git a/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardPlugin.java b/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardPlugin.java index 9f911d4..b971da8 100644 --- a/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardPlugin.java +++ b/finalguard/src/main/java/net/woggioni/gradle/finalguard/FinalGuardPlugin.java @@ -14,6 +14,7 @@ import org.gradle.api.tasks.compile.CompileOptions; import org.gradle.api.tasks.compile.JavaCompile; import javax.tools.Diagnostic; +import java.io.File; import java.net.URL; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -21,6 +22,7 @@ import java.util.jar.Manifest; public class FinalGuardPlugin implements Plugin { private static final String FINALGUARD_PLUGIN_CONFIGURATION = "finalguard_plugin"; private static final String JAVAC_PLUGIN_NAME = "net.woggioni.finalguard.FinalGuardPlugin"; + private static final String EXCLUDE_KEY = "exclude"; @Override public void apply(final Project project) { @@ -49,10 +51,12 @@ public class FinalGuardPlugin implements Plugin { }); final FinalGuardExtension finalGuardExtension = objects.newInstance(FinalGuardExtension.class); - extensionContainer.add("finalGuard", finalGuardExtension); + finalGuardExtension.getSkipGeneratedSources().convention(true); + extensionContainer.add("finalguard", finalGuardExtension); tasks.withType(JavaCompile.class, javaCompileTask -> { javaCompileTask.doFirst(t -> { final CompileOptions options = javaCompileTask.getOptions(); + options.setAnnotationProcessorPath(options.getAnnotationProcessorPath().plus(javacPluginConfiguration)); final StringBuilder xpluginArg = new StringBuilder("-Xplugin:").append(JAVAC_PLUGIN_NAME); appendOption(xpluginArg, "default.level", finalGuardExtension.getDefaultLevel()); appendOption(xpluginArg, "local.variable.level", finalGuardExtension.getLocalVariableLevel()); @@ -62,14 +66,27 @@ public class FinalGuardPlugin implements Plugin { 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 = options.getGeneratedSourceOutputDirectory().getAsFile().getOrNull(); + if (generatedSourceDir != null) { + appendOption(xpluginArg, EXCLUDE_KEY, generatedSourceDir.getAbsolutePath()); + } + } + for(final String excludedPrefix : finalGuardExtension.getExcludedPrefixes().get()) { + appendOption(xpluginArg, EXCLUDE_KEY, excludedPrefix); + } options.getCompilerArgs().add(xpluginArg.toString()); }); }); } + 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 property) { if (property.isPresent()) { - sb.append(' ').append(key).append('=').append(property.get()); + appendOption(sb, key, property.get().toString()); } } } diff --git a/gradle.properties b/gradle.properties index ef9de28..2134a37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ lys.catalog.version=2025.12.27 -version.myGradlePlugins=2026.02.17 +version.myGradlePlugins=2026.02.19 version.gradle=9.3.1 gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven diff --git a/jpms-check/build.gradle b/jpms-check/build.gradle index 030a2fa..d9fb2dd 100644 --- a/jpms-check/build.gradle +++ b/jpms-check/build.gradle @@ -2,6 +2,12 @@ plugins { id 'groovy-gradle-plugin' } +java { + sourceCompatibility(JavaVersion.VERSION_1_8.toString()) + targetCompatibility(JavaVersion.VERSION_1_8.toString()) + modularity.inferModulePath = false +} + gradlePlugin { plugins { create("JPMSCheckPlugin") {