Skip to content

fix: defer compile-classpath probes out of GroovyCompile task configuration - #16076

Merged
jdaugherty merged 1 commit into
apache:8.0.xfrom
codeconsole:fix/eager-classpath-resolve-8.0.x
Aug 1, 2026
Merged

fix: defer compile-classpath probes out of GroovyCompile task configuration#16076
jdaugherty merged 1 commit into
apache:8.0.xfrom
codeconsole:fix/eager-classpath-resolve-8.0.x

Conversation

@codeconsole

Copy link
Copy Markdown
Contributor

Problem

On Gradle 9.5+, running a task that depends on compileClasspath without also depending on the compile task — e.g. asset-pipeline's assetCompile — fails during task-graph construction:

Could not determine the dependencies of task ':assetCompile'.
> Could not resolve all dependencies for configuration ':compileClasspath'.
   > Could not create task ':compileGroovy'.
      > Could not resolve all dependencies for configuration ':compileClasspath'.
         > Cannot observe dependencies before markAsObserved(String) has been called.

The chain (observed from a Grails 8.0.0-M4 app on Gradle 9.6.1):

  1. Scheduling assetCompile resolves compileClasspath for build dependencies.
  2. Mid-resolve, Gradle freezes the configuration's attributes; the target-JVM attribute's provider chain realizes the compileGroovy task.
  3. Task realization fires GrailsGradlePlugin.configureGroovyCompiler's configureEach callback, which calls getGroovyCompilerScriptisClassOnClasspath(compile.classpath, …)classpath.files — re-entering the resolution already in flight.
  4. Gradle 9.5+'s stricter resolution state machine rejects the re-entrancy with the markAsObserved IllegalStateException. Older Gradle (9.4.x and below) tolerated it silently.

Invocations that realize the compile tasks during task selection (build, compileGroovy, or even assetCompile compileGroovy) are unaffected, which makes the failure look sporadic.

Fix

Move the star-import computation — including the two isClassOnClasspath classpath probes — inside the closure that getGroovyCompilerScript returns. That closure is only invoked from the task's doFirst, at execution time, where resolving the classpath is legal. Nothing at task-configuration time touches the classpath anymore.

The GrailsPluginGradlePlugin override already invokes the parent closure lazily inside its own closure (parent?.call() ?: ''), so it composes unchanged, and the call site's existing null handling covers the no-imports case.

Verification

  • :grails-gradle-plugins:compileGroovy passes.
  • Published the patched plugin locally and pointed the affected Grails 8.0.0-M4 app at it: ./gradlew assetCompile now constructs its task graph and runs (previously failed as above), and the generated grailsGroovyCompilerConfig-compileGroovy.groovy still contains all three star-import packages (jakarta.validation.constraints, grails.gorm.annotation, grails.plugin.scaffolding.annotation), confirming the deferred probes see the same classpath. Full app compile succeeds with the auto-imports in effect.

…ration

getGroovyCompilerScript ran its isClassOnClasspath probes eagerly in the
tasks.withType(GroovyCompile).configureEach callback, resolving the task's
compile classpath at task-configuration time. A GroovyCompile task can be
realized from inside an in-flight resolution of compileClasspath: scheduling
any task whose inputs include that configuration (e.g. asset-pipeline's
assetCompile) realizes the compile task through the target-JVM attribute's
provider chain. The callback then re-enters the resolution already in
flight, which Gradle 9.5+ rejects with

    IllegalStateException: Cannot observe dependencies before
    markAsObserved(String) has been called

failing the build with 'Could not determine the dependencies of task
:assetCompile'. Builds that realize the compile tasks during task selection
(build, compileGroovy) were unaffected, which is why the failure only shows
for tasks like assetCompile that depend on compileClasspath without
depending on the compile task.

Move the classpath probes (and the rest of the script generation) inside
the returned closure, which is only invoked from the task's doFirst at
execution time, where resolving the classpath is legal. The
GrailsPluginGradlePlugin override already invokes the parent closure
lazily inside its own closure, so it composes unchanged.
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 51.8562%. Comparing base (57883c4) to head (4fdb88b).

Files with missing lines Patch % Lines
...rails/gradle/plugin/core/GrailsGradlePlugin.groovy 0.0000% 12 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@               Coverage Diff                @@
##             8.0.x     #16076         +/-   ##
================================================
+ Coverage         0   51.8562%   +51.8562%     
- Complexity       0      18110      +18110     
================================================
  Files            0       2046       +2046     
  Lines            0      96274      +96274     
  Branches         0      16727      +16727     
================================================
+ Hits             0      49924      +49924     
- Misses           0      38980      +38980     
- Partials         0       7370       +7370     
Files with missing lines Coverage Δ
...rails/gradle/plugin/core/GrailsGradlePlugin.groovy 0.0000% <0.0000%> (ø)

... and 2045 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@testlens-app

testlens-app Bot commented Jul 31, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 4fdb88b
▶️ Tests: 57599 executed
⚪️ Checks: 60/60 completed


Learn more about TestLens at testlens.app.

@jdaugherty
jdaugherty merged commit 457f2fb into apache:8.0.x Aug 1, 2026
63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants