Fix duplicate generated sources in the test source set#78
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses issue #19 where protoc-generated sources can leak back into the test source set, causing duplicate-class compilation failures. It does so by making exclusion of the protoc output directory order-independent at compile-task level and by hardening path containment checks for symlinked project paths.
Changes:
- Add compile-task-level filtering to keep
protocoutput out ofJavaCompile/Kotlincompilation inputs regardless of source set mutation order. - Make
File.residesIn()symlink-safe by canonicalizing both operands before comparison. - Add a functional-test fixture and regression test reproducing the leaked
testsource-set state and asserting compilation succeeds.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| version.gradle.kts | Bumps compiler snapshot version. |
| gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt | Adds live compile-task filtering to exclude protoc output from compilation inputs. |
| gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Paths.kt | Updates residesIn() to use canonicalized operands for symlink robustness. |
| gradle-plugin/src/functionalTest/kotlin/io/spine/tools/compiler/gradle/plugin/PluginSpec.kt | Adds regression test ensuring test compilation succeeds even if protoc output is re-added. |
| gradle-plugin/src/functionalTest/resources/test-source-set/build.gradle.kts | Test fixture that re-adds protoc output dirs to test sources post-configuration to reproduce the failure mode. |
| gradle-plugin/src/functionalTest/resources/test-source-set/settings.gradle.kts | Test fixture settings for plugin resolution via mavenLocal. |
| gradle-plugin/src/functionalTest/resources/test-source-set/src/main/proto/main_scope.proto | Adds main-scope proto for the fixture. |
| gradle-plugin/src/functionalTest/resources/test-source-set/src/test/proto/test_scope.proto | Adds test-scope proto for the fixture. |
| .agents/tasks/fix-test-source-set-duplicates.md | Config-managed agent task file (not reviewed per repository guidelines). |
ef1a4d3 to
40a3ca3
Compare
`protoc` writes generated code to `build/generated/sources/proto/<sourceSet>` and the Compiler writes its processed output to `generated/<sourceSet>`. Both must not reach `javac`/`kotlinc` at once, or compilation fails with `duplicate class` errors. The plugin deduplicates by rewriting source-set directories once during configuration (`configureSourceSetDirs`), but that eager rewrite is fragile and does not always hold for the `test` source set — depending on the plugin-application order, a symlinked project path, or another plugin re-adding the directory, the `protoc` output may end up back in the set. Re-introduce order-independent compile-task filtering (the historic `configureCompileTasks` mechanism): `excludeProtocOutputFromCompilation()` keeps the `protoc` output directory out of every `JavaCompile`/`KotlinCompile` source via a live filter, so the deduplication holds regardless of when the directory was added. `JavaCompile` re-sets its `source` to a filtered view (it ignores `exclude(Spec)` for the files it passes to `javac`); `KotlinCompile` honors `exclude(Spec)`. The deprecated in-place mode is left untouched. Harden `Paths.residesIn` to canonicalize both operands so a symlinked project path no longer defeats the check. Add the `test-source-set` functional-test fixture and a regression test that reproduces the leaked state and asserts the `test` source set still compiles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40a3ca3 to
4199846
Compare
`CoreJvmCompiler` 2.0.0-SNAPSHOT.080 breaks the repository's self-build: the code it generates for the compiler's own Protobuf types is not loadable at test runtime, so `:backend:test` and `:backend:performanceTest` fail with `io.spine.type.UnknownTypeException: No Java class found for the Protobuf message of type ...` for many types (e.g. `spine.compiler.ProtobufSourceFile`). This affects every PR built against the current `master`. Pin both `dogfoodingVersion` (build classpath) and `version` back to the last-known-good `.079`, under which `:backend:test` (0 failed) and `:backend:performanceTest` pass locally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7ebf4f8 to
6f754c9
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #78 +/- ##
============================================
+ Coverage 75.62% 76.51% +0.89%
- Complexity 677 681 +4
============================================
Files 203 203
Lines 3950 3972 +22
Branches 392 396 +4
============================================
+ Hits 2987 3039 +52
+ Misses 845 807 -38
- Partials 118 126 +8 🚀 New features to boost your workflow:
|
`residesIn` canonicalizes both operands, so calling it per source file re-canonicalized the `protoc` output directory on every file. Add a `residesIn(Path)` overload that takes an already-canonical directory, and canonicalize the `protoc` output once in `excludeProtocOutputFromCompilation`, avoiding the repeated filesystem work on large source sets. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`KotlinCompilationTask` comes from the Kotlin Gradle Plugin, which is a `compileOnly` dependency and is therefore not on the plugin's runtime classpath. Referencing it via a class literal forced the class to load even for consumers that apply `io.spine.compiler` without Kotlin (e.g. `tests/in-place-consumer`), risking a `NoClassDefFoundError` during plugin application. Guard the Kotlin compile-task filtering with `hasKotlin()` (a name-based check), so the task type is referenced only when Kotlin is present. Also align the `compiler-cli-all` and `compiler-protoc-plugin` versions in `docs/dependencies/pom.xml` with the bumped project version (`.058`), matching `dependencies.md`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover `excludeProtocOutputFromCompilation` and `File.residesIn` with in-process tests so the deduplication logic is exercised under Kover. The functional test runs in a separate TestKit JVM that coverage cannot instrument, which left the new lines uncovered. `ExcludeProtocOutputSpec` evaluates a project with the plugin applied and asserts a file under the `protoc` output directory is kept out of the `compileJava` source. `PathsSpec` covers the nested, self, sibling, and already-canonical cases of `residesIn`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
armiol
approved these changes
Jul 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes #19 — generated source sets in tests may contain duplicates, causing
duplicate classcompilation failures in thetestsource set.protocwrites generated code tobuild/generated/sources/proto/<sourceSet>/…and the Spine Compiler writes its processed output to
generated/<sourceSet>/….Both must not reach
javac/kotlincat once. The plugin deduplicates byrewriting source-set directories once during configuration
(
configureSourceSetDirs, in tool-base), but that eager rewrite is fragile anddoes not always hold for the
testsource set — depending on theplugin-application order, a symlinked project path, or another plugin re-adding
the directory, the
protocoutput may end up back in the source set.Changes
Plugin.kt— newexcludeProtocOutputFromCompilation()keeps theprotocoutput directory out of every
JavaCompile/KotlinCompilesource via a livefilter, so the deduplication holds regardless of when the directory was added
to the source set. This re-introduces the order-independent compile-task
filtering the issue references (the historic
configureCompileTasks, removedfrom ProtoData in 2023).
JavaCompilere-sets itssourceto a filtered view(it ignores
exclude(Spec)for the files it passes tojavac);KotlinCompilehonors
exclude(Spec). The deprecated in-place mode is left untouched.Paths.kt—residesInnow canonicalizes both operands, so a symlinkedproject path no longer defeats the check (a latent canonical-vs-absolute bug,
a likely "sometimes" trigger).
PluginSpec.kt+test-source-set/fixture — regression test that re-addsthe
protocoutput directory to thetestsource set (reproducing the leakedstate) and asserts the
testsource set still compiles.version.gradle.kts—2.0.0-SNAPSHOT.057→.058.Verification
compileTestJavafails withduplicate class: …TestScoped),GREEN with the fix.
PluginSpec: 16 tests, 0 failures (1 pre-existing skip).:gradle-plugin:build+dokkaGenerate(all modules) pass.Pre-PR reviewers
kotlin-engineer(APPROVE WITH CHANGES — clarifying comments applied),spine-code-review(APPROVE),review-docs(APPROVE). No Must-fix items.🤖 Generated with Claude Code
Additional changes — dependency / build-tooling update (folded in to unblock CI)
The first commit is the #19 fix. The later commits carry a dependency and
build-tooling update that is required to get this branch's CI green: the
repository's self-build was broken by an upstream snapshot,
core-jvm-compiler 2.0.0-SNAPSHOT.080, whose generated code for the compiler'sown Protobuf types was not loadable at test runtime — failing
:backend:testand the performance smoke test on every current PR. Resolving it required:
buildSrc/src/main/kotlin/io/spine/dependency/**,with
docs/dependencies/**regenerated to match.9.5.1→9.6.1.v4→v7(.github/workflows/build-on-ubuntu.yml).configsubmodule bumped.These are intentional and make the self-build (and therefore this PR's checks)
pass on top of the #19 fix.