codeconsole opened a new pull request, #16170:
URL: https://github.com/apache/grails-core/pull/16170

   Groovy compiles dynamic dispatch either as `invokedynamic` or as call-site 
caching bytecode. Which is faster depends on the application, but the choice 
was previously fixed at compile time and could not reach the framework and 
plugin jars an application already depends on.
   
   This publishes every Groovy module and Grails plugin twice: a default 
artifact using Groovy's own `invokedynamic` default, and a `noindy` classifier 
artifact compiled without it. An application sets `grails.indy` and the choice 
propagates across the whole dependency graph.
   
   ### Why not the classifier alone
   
   A Maven classifier artifact shares the POM of the main artifact, so asking 
for one by classifier pulls the *default* flavour of everything it depends on — 
one noindy jar in a graph of indy jars. The noindy artifact is instead 
published as a **secondary variant** of `apiElements`/`runtimeElements`, which 
inherits their dependencies, so a single request applies transitively. The 
classifier remains how the artifact is packaged, not how it is selected.
   
   ### Only the noindy variants declare the attribute
   
   The default variants deliberately leave `org.apache.grails.indy` unset. A 
consumer that never requests it — any plain Gradle project not applying a 
Grails plugin — sees one candidate and resolves the default artifact exactly as 
before. Declaring it on both variants makes every such build fail with `There 
are several available matching variants`, which an earlier revision of this 
branch did.
   
   A dependency publishing a single artifact (a plugin built before this, or 
one opting out) stays resolvable under either setting, since the two flavours 
interoperate on the same classpath.
   
   ### Default now follows Groovy
   
   `grails.indy` defaulted to `false`; it now defaults to `true`. Native 
compilation is the reason: call-site caching bytecode links call sites at 
runtime and cannot be compiled ahead of time, so every artifact on a native 
application's classpath must be the `invokedynamic` flavour. `indy = false` is 
an opt-out that forfeits native compilation.
   
   This also resolves an existing incoherence — the framework currently ships 
mixed bytecode (`grails-controllers` indy-on, `grails-gsp` indy-off) purely 
from which modules happened to adopt `grails-plugin`.
   
   `grails.indy` no longer governs how a plugin compiles its own sources, since 
a plugin builds both flavours and the resolving application chooses.
   
   ### Tests
   
   8 functional tests in `GrailsIndyVariantsSpec` covering: both flavours 
compiled and byte-different, variants published on both element configurations, 
application selection, fallback for single-artifact dependencies, the new 
default, and that a plain Gradle consumer is unaffected.
   
   Verified on a real framework module: `AllowedMethodsHelper.class` carries 2 
`invokedynamic` instructions in the default jar and 0 in the noindy jar. 
`grails-cache`, where both producer paths can reach the same project, builds 
one `noindyJar` without collision.
   
   ### Reviewer notes
   
   - Full `./gradlew build` has not been run — 2 of ~137 modules were built 
locally.
   - The cost is real: Groovy compilation roughly doubles, as does the artifact 
count per release (signing, SBOM, vulnerability scan).
   - Given the native constraint, an alternative worth weighing is dropping 
dual publication entirely and making `indy` an application-only option so every 
published artifact is `invokedynamic`. A companion PR follows with that 
approach.


-- 
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