Skip to content

Add CodeGeneratorRequestWriter from Base; fix plugins to work with Gradle cache; fix ArtifactMetaPlugin working directory#181

Merged
alexander-yevsyukov merged 17 commits into
masterfrom
claude/busy-dirac-wwflbm
Jun 11, 2026
Merged

Add CodeGeneratorRequestWriter from Base; fix plugins to work with Gradle cache; fix ArtifactMetaPlugin working directory#181
alexander-yevsyukov merged 17 commits into
masterfrom
claude/busy-dirac-wwflbm

Conversation

@alexander-yevsyukov

@alexander-yevsyukov alexander-yevsyukov commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This is the receiving half of SpineEventEngine/base-libraries#938: CodeGeneratorRequestWriter is protoc-plugin tooling, not runtime API, so it moves from base into the tool-base module.

Changes

  • Add CodeGeneratorRequestWriter under the new package io.spine.tools.code.proto (repackaged from io.spine.code.proto to avoid a split package across the spine-base and spine-tool-base artifacts, as discussed in the issue).
  • Add CodeGeneratorRequestWriterSpec with a local constructRequest helper (in base the helper is shared with the parsing specs, which stay there).
  • Fix code generation with the Gradle build cache enabled (see below).
  • Make ArtifactMetaPlugin write its files under build/spine/artifact-meta instead of claiming the whole build/spine directory (see below).
  • Bump version → 2.0.0-SNAPSHOT.400.

Gradle build cache fix

With org.gradle.caching=true, clean build failed in modules using the io.spine.generated-sources and io.spine.descriptor-set-file plugins (e.g., classic-codegen in this repository): compilation reported unresolved references because no generated code was present.

Root cause

The same pattern as SpineEventEngine/compiler#67. Both plugins do their work in doLast actions of the cacheable generateProto task without declaring the results as task outputs:

  • GeneratedSourcePlugin copies the protoc output into $projectDir/generated/<sourceSet>/;
  • DescriptorSetFilePlugin creates the desc.ref file next to the descriptor set file.

clean deletes generated/ and build/; the build cache then restores generateProto without executing it — only declared outputs are restored — so the copied sources and desc.ref are missing, and compilation fails. With the cache off, everything works only because the task always re-executes after clean.

Fix

  • GeneratedSourcePlugin — declares $projectDir/generated/<sourceSet> as an output of generateProto, so the build cache stores and restores the copied code.
  • DescriptorSetFilePlugin — declares desc.ref as an output (the .desc file itself is already a declared output of the Protobuf Gradle plugin).
  • The generated source directories are now added to source sets via project.files(dir).builtBy(task). This fixes a latent gap exposed by the new test: in pure-Java projects compileJava did not depend on generateProto at all, because the plugin severs the dependency carried by the protoc output directories when replacing them with generated/.... In Spine repositories this was masked by the Kotlin compilation dependency arranged by setupKotlinCompile().

Testing

The new BuildCacheSpec runs buildcleanbuild via TestKit against a project applying both plugins, with a project-local build cache directory for hermeticity. The test project's clean deletes generated/ (mimicking the Spine convention), and a handwritten Java class refers to the generated code, so missing codegen fails the compilation.

  • Cache on: asserts generateProto is FROM_CACHE, the generated code, desc.ref, and the descriptor set file are restored and re-packed into resources.
  • Cache off: asserts the task re-executes normally after clean.

Verified both ways: with the output declarations temporarily removed, the cache-on test fails while the cache-off test passes; with the fix, both pass.

ArtifactMetaPlugin working directory

The plugin declared the whole build/spine directory as the output of its writeArtifactMeta task and as a main resources source directory. build/spine is the root working directory shared by all Spine plugins (see rootWorkingDir), where each plugin is expected to claim its own subdirectory. Claiming the root put the declared inputs of other Spine plugins' tasks — e.g., build/spine/compiler/{requests,settings} of launchSpineCompiler — inside the declared output tree of writeArtifactMeta. Gradle's missing-dependency validation then failed consumer builds:

Task ':base:launchSpineCompiler' uses this output of task ':base:writeArtifactMeta' without declaring an explicit or implicit dependency.

The actual files never intersected; only the declared trees overlapped.

The plugin now claims build/spine/artifact-meta. The metadata file keeps its path relative to the output directory, so the in-JAR resource path — META-INF/io.spine/<artifact-id>.meta, the contract of ArtifactMeta.loadFromResource — is unchanged, and the reading side needs no changes. A side benefit: working files of other plugins under build/spine can no longer be swept into packaged resources by processResources. Consumer repositories can drop mustRunAfter("writeArtifactMeta") workarounds once they depend on a tool-base version with this change.

Notes

  • org.gradle.caching remains off in gradle.properties: this repository's own build applies the published protobuf-setup-plugins via the root buildscript classpath, which does not yet contain the fix. Re-enable once ToolBase.version in buildSrc points to a release with this change.
  • In repositories where the Spine Compiler also writes into generated/<sourceSet> (e.g., validation), Gradle's overlapping-outputs detection makes it re-execute the affected tasks instead of restoring them — correct results, fewer cache hits. Separating output directories per producer is a follow-up beyond this PR.

https://claude.ai/code/session_01QkiJSc86Y7ocs1PeZMxJhX

🤖 Generated with Claude Code

claude added 4 commits June 10, 2026 03:20
The class is protoc-plugin tooling: its only consumers are the
protoc-plugin entry points of the Compiler and ProtoTap, both of which
already depend on ToolBase. It is repackaged from `io.spine.code.proto`
to `io.spine.tools.code.proto` to avoid a split package across the
`spine-base` and `spine-tool-base` artifacts.

See SpineEventEngine/base-libraries#938
claude added 2 commits June 10, 2026 03:30
The object is `internal` to `base`, so the moved class can no longer
import it for the documentation link. The `parse` extension applies
the registry internally; the KDoc now says so in prose.
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 90.45%. Comparing base (e50738d) to head (d7085b4).

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #181      +/-   ##
============================================
+ Coverage     90.40%   90.45%   +0.04%     
  Complexity      554      554              
============================================
  Files           122      122              
  Lines          2230     2241      +11     
  Branches        311      311              
============================================
+ Hits           2016     2027      +11     
  Misses           93       93              
  Partials        121      121              
🚀 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 Jun 10, 2026
@alexander-yevsyukov alexander-yevsyukov moved this to 🏗 In progress in v2.0 Jun 10, 2026
@alexander-yevsyukov alexander-yevsyukov marked this pull request as ready for review June 10, 2026 16:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b5b01c61b

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tool-base/src/main/kotlin/io/spine/tools/code/proto/CodeGeneratorRequestWriter.kt Outdated
alexander-yevsyukov and others added 6 commits June 10, 2026 17:24
`GeneratedSourcePlugin` and `DescriptorSetFilePlugin` do their work in
`doLast` actions of the cacheable `generateProto` task. Declare the
copied `generated/<sourceSet>` directory and the `desc.ref` file as
task outputs so that the build cache stores and restores them after
`clean`.

Also add the generated source directories to source sets via
`builtBy(task)` so that the consuming tasks (e.g., `compileJava` in
pure-Java projects) depend on the generating task.

The new `BuildCacheSpec` verifies both plugins with the build cache
turned on and off. `org.gradle.caching` stays off in this repository
until the dogfooded `protobuf-setup-plugins` contains this fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A bare file name passed as the request file path has no parent
directory, making `File.parentFile` return `null`. Guard the parent
directory creation so that `writeBinary()` and `writeJson()` work for
files resolved against the current working directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alexander-yevsyukov alexander-yevsyukov changed the title Add CodeGeneratorRequestWriter moved from Spine Base Add CodeGeneratorRequestWriter from Base; fix plugins to work with Gradle cache Jun 10, 2026
`ArtifactMetaPlugin` claimed the whole `build/spine` directory — the
root working directory shared by all Spine plugins — as the output of
its `writeArtifactMeta` task and as a `main` resources source
directory.

Declared inputs of other plugins' tasks under `build/spine`, such as
`build/spine/compiler/settings` of `launchSpineCompiler`, were thus
contained in the declared output of `writeArtifactMeta`, making Gradle
demand explicit ordering between tasks of unrelated plugins:

  Task ':launchSpineCompiler' uses this output of task
  ':writeArtifactMeta' without declaring an explicit or implicit
  dependency.

The plugin now claims the dedicated `build/spine/artifact-meta`
subdirectory, following the `rootWorkingDir` convention. The path of
the metadata file relative to the output directory is unchanged, so the
in-JAR resource path (`META-INF/io.spine/<artifact-id>.meta`) read by
`ArtifactMeta.loadFromResource` stays the same, and the reading side
needs no changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alexander-yevsyukov alexander-yevsyukov changed the title Add CodeGeneratorRequestWriter from Base; fix plugins to work with Gradle cache Add CodeGeneratorRequestWriter from Base; fix plugins to work with Gradle cache; fix ArtifactMetaPlugin working directory Jun 10, 2026
@alexander-yevsyukov alexander-yevsyukov changed the title Add CodeGeneratorRequestWriter from Base; fix plugins to work with Gradle cache; fix ArtifactMetaPlugin working directory Add CodeGeneratorRequestWriter from Base; fix plugins to work with Gradle cache; fix ArtifactMetaPlugin working directory Jun 10, 2026
@alexander-yevsyukov alexander-yevsyukov merged commit 0d3ae9c into master Jun 11, 2026
8 checks passed
@alexander-yevsyukov alexander-yevsyukov deleted the claude/busy-dirac-wwflbm branch June 11, 2026 08:56
@github-project-automation github-project-automation Bot moved this from 🏗 In progress to ✅ Done in v2.0 Jun 11, 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.

3 participants