codeconsole commented on PR #16114:
URL: https://github.com/apache/grails-core/pull/16114#issuecomment-5289016409

   Both questions landed on something real, and chasing the second one led back 
to the first. The
   answer to both turned out to be to stop reading the classpath at all.
   
   ## Why the classpath was involved
   
   `isClassOnClasspath` opened jars on the compile classpath to decide whether 
to add the
   `grails.gorm.annotation` and `grails.plugin.scaffolding.annotation` star 
imports. That one probe
   forced everything else: the script could only be built at execution time, 
which is why it was
   computed in a task action, why the task opted out of state tracking, and why 
it needed an ordering
   edge to whatever produced the classpath.
   
   The probe bought nothing. A star import of a package that is not on the 
classpath contributes no
   classes and is not an error in Groovy — verified on 5.0.8 for both dynamic 
and `@CompileStatic`
   compilation. `jakarta.validation.constraints` in the same block was already 
unconditional; the two
   probed packages were the outlier. They are unconditional now too.
   
   That is the one behavioural change here. Given this is 8.0.x it seemed the 
right moment, but it is
   easy to put back if you would rather keep the probe.
   
   ## On the design you reverted in 9a529d1996
   
   Your concern was well founded and worth being precise about.
   
   That design declared the runtime classpath as a **task input**:
   
   ```groovy
   task.inputs.files(project.configurations.named('runtimeClasspath'))
   ```
   
   An input has to be snapshotted, so `runtimeClasspath` was resolved for the 
up-to-date check and
   became part of the fingerprint — which is where "task dependency errors if 
the runtimeclasspath is
   changed" comes from.
   
   The generator now reads no classpath at all. It takes the finished script as 
a single `@Input` and
   writes it:
   
   ```
   GENERATOR_DEPENDS_ON_COMPILE_CLASSPATH=false
   GENERATOR_DEPENDS_ON_RUNTIME_CLASSPATH=false
   GENERATOR_DECLARED_INPUT_FILES=0
   ```
   
   So no configuration enters its dependency chain or its up-to-date check. The 
hazard you removed is
   structurally absent rather than narrowly avoided, and the task is properly 
tracked rather than
   `doNotTrackState` — an unchanged build is `UP-TO-DATE`, changing the imports 
regenerates.
   
   Separately, and not caused by this PR: mutating the runtime classpath from a 
late callback still
   fails in a Grails app today, with
   
   ```
   Cannot mutate the dependencies of configuration ':runtimeOnly' after the 
configuration's
   child configuration ':grailsCliDetect' was resolved.
   ```
   
   That reproduces identically on `8.0.x` without this branch — it comes from 
the CLI companion probe,
   not from the compiler config wiring. Happy to raise it separately if that is 
news.
   
   ## On whether Gradle's restriction is artificial
   
   I don't think a ticket would go anywhere, because Gradle shipped the 
replacement rather than closing
   the door. `GroovyCompileOptions.getConfigurationScript()` is annotated
   `@ReplacedBy("configurationScriptFile")`, and `getConfigurationScriptFile()` 
returns a
   `RegularFileProperty`. Lazy task properties are finalized before execution 
so inputs can be
   fingerprinted for up-to-date checks and the configuration cache; that is the 
point of the migration,
   not an incidental restriction. The branch now uses that property.
   
   The `doFirst` was also quietly wrong before 9.7. It declared the script as 
an **output** of the
   compile task and assigned it after input snapshotting, so the script never 
took part in
   `compileGroovy`'s up-to-date check — change what the script generated and 
the compile task would not
   re-run.
   
   ## A regression this turned up
   
   Worth flagging on its own. 
`GrailsPluginGradlePlugin.getGroovyCompilerScript` registers the project
   version and name as inputs of the compile task, because it bakes them into 
compiled classes as AST
   metadata. When the script computation moved into a task action, those 
`inputs.property` calls ran too
   late to take effect:
   
   | commit | `compileGroovy` has a `version` input |
   |---|---|
   | 3e4fa5c897 (before this PR) | true |
   | 79504824fd (this PR as first pushed) | **false** |
   | now | true |
   
   So as first pushed, changing a plugin's version would have left the stamped 
metadata stale. Building
   the script during configuration restores it, and there is a test for it.
   
   ## Where it ended up
   
   The generator is a `GrailsCompilerConfigScriptTask` with an `@Input 
Property<String>` and an
   `@OutputFile`. It reads no classpath, declares no file inputs, is up-to-date 
checked, and stores and
   reuses a configuration cache entry. It waits for a task that produces the 
build's own
   `configurationScript`, if there is one — without that edge the build's 
script was silently dropped on
   a clean build and only appeared from the second build onwards.
   
   Seven tests in `GrailsGroovyCompilerConfigSpec` cover the lifecycle 
properties. `:grails-gradle:test`,
   `codeStyle` and a full root `build -PskipTests` are green on 9.7.0.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to