Skip to content

Find tasks lazily in plugin-base and protobuf-setup-plugins - #199

Merged
alexander-yevsyukov merged 13 commits into
masterfrom
claude/lazy-task-lookups-58137b
Oct 29, 2026
Merged

alexander-yevsyukov merged 13 commits into
masterfrom
claude/lazy-task-lookups-58137b

Conversation

@alexander-yevsyukov

@alexander-yevsyukov alexander-yevsyukov commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Closes #198.

plugin-base and protobuf-setup-plugins no longer create tasks that a build may never run. Each call site listed in the issue is now lazy. So is one more that the issue's measurement hid. The PR also fixes a ConcurrentModificationException that broke builds applying idea together with these plugins.

This is a breaking change for plugin-base, because it removes the unused JavaCompileTasks. So the version is 2.0.0-SNAPSHOT.430.

New API

In plugin-base, io.spine.tools.gradle.project (ProjectExts.kt):

  • Project.javaCompileTasksFor(sourceSet): TaskCollection<JavaCompile>
  • Project.kotlinCompileTasksFor(sourceSet): TaskCollection<Task>. It matches by name only, so it works when the Kotlin Gradle plugin is not on the classpath (the Compiler has it as compileOnly). Narrow it with withType(KotlinCompilationTask::class.java) where needed.
  • findJavaCompileFor() and findKotlinCompileFor() are deprecated but keep working.

GradleTask.getTaskProvider() returns the provider of the task without creating it.

SpineEventEngine/compiler#99 and SpineEventEngine/core-jvm-compiler#123 fix their own call sites against ToolBase .428, so they don't depend on this PR. To remove the duplicated logic, these repos should adopt the API when they bump ToolBase to .430:

  • compiler: createTasks() derives compileJavaTaskName and getCompileTaskName("Kotlin") itself. Use javaCompileTasksFor() and kotlinCompileTasksFor() instead, and keep the KSP task lookup.
  • core-jvm-compiler: CommonKspSettingsPlugin still calls the deprecated findKotlinCompileFor() in afterEvaluate, which creates compile*Kotlin. Use kotlinCompileTasksFor().

Important

Gradle throws DefaultTaskCollection#configureEach(Action) on task set cannot be executed in the current context when configureEach {} or TaskProvider.configure {} is called from inside the configuration action of another task. Call the new functions where the task is registered, not inside its register {} block. The Compiler's LaunchSpineCompiler.setDependencies() runs inside such a block today.

Changes

  • findJavaCompileFor() / findKotlinCompileFor(): deprecated in favour of the lazy functions above.
  • DescriptorSetFilePlugin.dependOnProcessResourcesTask(): removed. The issue suggested withType<ProcessResources>().named { … }.configureEach {}, but that cannot work here. setup() runs inside the configuration action of GenerateProtoTask, where Gradle forbids configuring other tasks (see the note above). Instead, the descriptors directory is added to sourceSet.resources as files(dir).builtBy(generateProto). processResources, and any other consumer of the resources, depends on generateProto through its inputs.
  • GradleTask.Builder.applyNowTo(): registers the task and wires the dependencies through providers. The following task gets named(…).configure { dependsOn(newTask) }.
    • It now looks up the following task before registering the new one, so a missing anchor no longer leaves the new task registered.
    • It copies the builder state, so later changes to the builder don't affect the registered task.
    • insertAfterAllTasks() still covers only the tasks registered when applyNowTo() runs. It checks their names, which does not create them, and depends on their providers.
    • GradleTask and its builder are now deprecated. Configure preClean and the gRPC generateProto tasks lazily core-jvm-compiler#123 replaces the last known use (CleaningPlugin) with tasks.register and named("clean").configure { dependsOn(…) }, which does the same without the wrapper. The lazy implementation stays for any remaining callers.
  • GeneratedSourcePlugin: setup() no longer calls setupKotlinCompile(), which created compile*Kotlin. This call site is not in the issue. It sat behind the Compiler's findKotlinCompileFor(), so the Compiler's call was recorded as the first one to create the task. Since cdeae452, the generated source directories already make compilation depend on generateProto. setupKotlinCompile() is deprecated, and its body is unchanged.
  • ArtifactMetaPlugin (jvm-tool-plugins): it no longer creates sourcesJar via findByName() in afterEvaluate. It makes the task depend on writeArtifactMeta via tasks.named { … }.configureEach {}, which also includes a sourcesJar registered after the plugin is applied. The plugin is applied by the Spine tool repos' own builds, not by consumers like core-jvm.
  • JavaCompileTasks: removed, along with its test and the test helpers that served only it. Nothing in the SpineEventEngine organization used it, and its addArgs() created every JavaCompile task of the project.
  • TaskDependencies.dependsOn() (and so GradleTruth's dependsOn) now recognizes TaskProvider and TaskCollection entries of getDependsOn() without creating tasks. Before, dependencies declared through providers were reported as missing.
  • idea + io.spine.generated-sources: the build failed at configuration with ConcurrentModificationException. This also happened on master. The Protobuf plugin's own afterEvaluate reads the source directories for the IDE, and doing so first created generateProto. setup() then modified the directories being iterated. ProtobufSetupPlugin now creates the tasks in a protobuf.generateProtoTasks {} action, which the Protobuf plugin runs first, as the Compiler already does. The afterEvaluate block stays for tasks registered later. As before this PR, the generate*Proto tasks are created in every build: setup() must change the source sets before anything reads them.

Verification

  • ProjectBuilder specs record created tasks via tasks.configureEach {}. They check that the wiring creates no tasks and that the dependencies still resolve: ProjectExtsSpec, GradleTaskBuilderSpec, GradleTaskTest, TaskDependenciesSpec and LazyTaskWiringSpec.
  • A TestKit build in LazyTaskWiringSpec applies idea, KGP and both plugins.
    • ./gradlew help creates only generateProto, generateTestProto, help and clean.
    • compileKotlin --dry-run still schedules generateProto.
  • Mutation checks confirm the tests catch the old behaviour:
    • Putting back the setupKotlinCompile() call makes the help check fail with compile*Kotlin created.
    • The processResources spec fails against the old DescriptorSetFilePlugin.
  • New TestKit cases in GeneratedSourcePluginSpec and DescriptorSetFilePluginSpec apply idea. The GeneratedSourcePlugin case failed with the CME before the fix.
  • ./gradlew build dokkaGenerate passes.

Rollout

Consumers load tool-base classes from the core-jvm-plugins fat JAR of core-jvm-compiler. The fixes in tool-base's own plugins (process*Resources and compile*Kotlin no longer created, the idea crash) reach them in this order:

  1. Merge this PR, which publishes ToolBase 2.0.0-SNAPSHOT.430.
  2. compiler bumps ToolBase to .430, adopts the API above, and publishes. This PR changes when ProtobufSetupPlugin creates the GenerateProtoTasks, and the Compiler relies on that timing, so its integration tests check this change first.
  3. core-jvm-compiler bumps ToolBase and Compiler, adopts the API above, and republishes core-jvm-plugins.

🤖 Generated with Claude Code

alexander-yevsyukov and others added 5 commits October 29, 2026 18:20
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stop creating tasks which a build may never run (#198):

- Add `Project.javaCompileTasksFor()` and `kotlinCompileTasksFor()`,
  returning live `TaskCollection`s, and deprecate `findJavaCompileFor()`
  and `findKotlinCompileFor()`. The Kotlin variant matches by name, so it
  works without the Kotlin Gradle plugin on the classpath.
- Make `GradleTask.Builder.applyNowTo()` register the task and wire its
  dependencies via providers. `GradleTask` now holds a `TaskProvider`,
  exposed via `getTaskProvider()`. The builder state is copied, and the
  following task is checked before the new task is registered.
- `DescriptorSetFilePlugin` adds the descriptors directory to the
  resources as built by `GenerateProtoTask` instead of iterating over
  all `ProcessResources` tasks. `setup()` runs inside the configuration
  action of `GenerateProtoTask`, where Gradle forbids `configureEach()`
  on other tasks.
- `GeneratedSourcePlugin` no longer calls `setupKotlinCompile()`, which
  created the Kotlin compilation tasks. The generated source directories
  already make compilation depend on `GenerateProtoTask`.
- `TaskDependencies.dependsOn()` recognizes task providers and task
  collections.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the `idea` plugin applied, the Protobuf plugin reads the source
directories of the source sets once the project is evaluated, to add
them to the IDE sources. Doing so created `GenerateProtoTask` for the
first time, and `GeneratedSourcePlugin.setup()` modified the directories
being iterated, failing the build with `ConcurrentModificationException`.

`ProtobufSetupPlugin` now creates the tasks in a `generateProtoTasks {}`
action, which the Protobuf plugin runs first, like the Spine Compiler
does. The `afterEvaluate` block creating the tasks is kept for the tasks
registered later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Avoid `!!` in `GradleTaskBuilderSpec`.
- Name the forced creation of `GenerateProtoTask`s in
  `ProtobufSetupPlugin` and explain why both creation points exist.
- Say that `setupKotlinCompile()` is no longer needed.
- Make the `GradleTask` Javadoc precise about when the task is created
  and which projects `insertAfterAllTasks()` covers.
- Share `writeSampleProto()` via `ProtobufPluginTest`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 29, 2026 18:34
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T18:35:57.400534Z a09224e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

The review reports no active issues requiring resolution before approval.

No open issues 🎉

What changed in this PR

This PR reduces unnecessary task creation in ToolBase’s Gradle plugins, addressing #198, and fixes the configuration failure when generated-source setup runs alongside the idea plugin.

Changes:

  • Adds lazy compile-task lookup APIs and provider-based task registration and dependency checks.
  • Preserves compilation and resource dependencies through generated directories, and adjusts Protobuf setup timing for IDE compatibility.
  • Adds regression coverage and updates the publication version and dependency reports to 2.0.0-SNAPSHOT.429.
File Description
version.gradle.kts Bumps the publication version to .429.
protobuf-setup-plugins/​src/​test/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​ProtobufPluginTest.kt Adds a shared sample-proto helper.
protobuf-setup-plugins/​src/​test/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​LazyTaskWiringSpec.kt Tests lazy task wiring and Kotlin compilation dependencies.
protobuf-setup-plugins/​src/​test/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​GeneratedSourcePluginSpec.kt Adds generated-source coverage with idea applied.
protobuf-setup-plugins/​src/​test/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​DescriptorSetFilePluginSpec.kt Adds descriptor-generation coverage with idea applied.
protobuf-setup-plugins/​src/​test/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​BuildCacheSpec.kt Reuses the shared sample-proto helper.
protobuf-setup-plugins/​src/​main/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​ProtobufSetupPlugin.kt Initializes generation tasks before IDE source-directory inspection.
protobuf-setup-plugins/​src/​main/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​GeneratedSourcePlugin.kt Removes eager Kotlin task setup and deprecates its helper.
protobuf-setup-plugins/​src/​main/​kotlin/​io/​spine/​tools/​protobuf/​gradle/​plugin/​DescriptorSetFilePlugin.kt Wires resource dependencies through a directory marked builtBy.
plugin-base/​src/​test/​kotlin/​io/​spine/​tools/​gradle/​task/​TaskDependenciesSpec.kt Tests dependency detection through providers and collections.
plugin-base/​src/​test/​kotlin/​io/​spine/​tools/​gradle/​task/​GradleTaskTest.kt Checks provider access for an existing task.
plugin-base/​src/​test/​kotlin/​io/​spine/​tools/​gradle/​task/​GradleTaskBuilderSpec.kt Tests lazy registration, builder snapshots, and dependency wiring.
plugin-base/​src/​test/​kotlin/​io/​spine/​tools/​gradle/​project/​ProjectExtsSpec.kt Tests lazy compile-task lookup and configuration.
plugin-base/​src/​main/​kotlin/​io/​spine/​tools/​gradle/​project/​ProjectExts.kt Adds lazy compile-task collections and deprecates eager lookups.
plugin-base/​src/​main/​java/​io/​spine/​tools/​gradle/​task/​TaskDependencies.java Recognizes provider and collection dependencies without creating tasks.
plugin-base/​src/​main/​java/​io/​spine/​tools/​gradle/​task/​GradleTask.java Uses task providers and snapshots builder configuration.
docs/​dependencies/​pom.xml Updates the documented project version.
docs/​dependencies/​dependencies.md Refreshes report versions and generation timestamps.

Review effort: Balanced

@codecov

codecov Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.08%. Comparing base (c8a4562) to head (c97b78c).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #199      +/-   ##
============================================
- Coverage     92.18%   92.08%   -0.11%     
- Complexity      841      846       +5     
============================================
  Files           163      162       -1     
  Lines          3172     3182      +10     
  Branches        373      381       +8     
============================================
+ Hits           2924     2930       +6     
- Misses          116      122       +6     
+ Partials        132      130       -2     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alexander-yevsyukov alexander-yevsyukov self-assigned this Oct 29, 2026
@alexander-yevsyukov alexander-yevsyukov moved this to 🏗 In progress in v2.0 Oct 29, 2026
alexander-yevsyukov and others added 8 commits October 29, 2026 18:46
Covers the branches where a task provider or a task collection is checked after the dependency is already found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last known use of `GradleTask.Builder`, `CleaningPlugin` of
core-jvm-compiler, now registers its task via the Gradle API directly
(SpineEventEngine/core-jvm-compiler#123). `TaskContainer.register()` and
`TaskProvider.configure()` wire a task into the build lifecycle lazily
without the wrapper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The class has no users in the SpineEventEngine organization, only its
own test. Its `addArgs()` also created every `JavaCompile` task of the
project. The test helpers `ProjectTasks` and `ProjectConfigurations`
served only that test, so they are removed too.

This removes public API of `plugin-base`, so the version is rounded up
as a breaking change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`afterEvaluate { tasks.findByName(sourcesJar) }` created the task in
every build. A `named(Spec)` collection sets the dependency without
creating the task, and includes a `sourcesJar` registered after the
plugin is applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alexander-yevsyukov
alexander-yevsyukov enabled auto-merge (squash) October 29, 2026 19:29
@alexander-yevsyukov
alexander-yevsyukov merged commit b12e440 into master Oct 29, 2026
12 checks passed
@alexander-yevsyukov
alexander-yevsyukov deleted the claude/lazy-task-lookups-58137b branch October 29, 2026 19:39
@github-project-automation github-project-automation Bot moved this from 🏗 In progress to ✅ Done in v2.0 Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Find tasks lazily in plugin-base and protobuf-setup-plugins

2 participants