[
https://issues.apache.org/jira/browse/GROOVY-12683?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18139172#comment-18139172
]
ASF GitHub Bot commented on GROOVY-12683:
-----------------------------------------
paulk-asert merged PR #3204:
URL: https://github.com/apache/groovy/pull/3204
> Extension modules whose classes need an optional dependency break all
> compilation when that dependency is missing (groovy-fx in the distribution)
> -------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12683
> URL: https://issues.apache.org/jira/browse/GROOVY-12683
> Project: Groovy
> Issue Type: Bug
> Components: Extension methods, Grape
> Reporter: Paul King
> Priority: Major
>
> h3. Problem
> The {{groovy-fx}} module added by GROOVY-11434 is an optional module, but it
> is still copied into {{lib}} of the binary distribution and the
> {{installGroovy}} task output. JavaFX is a compile-only dependency of that
> module and is not bundled. On a JDK without JavaFX, that one jar makes
> *every* compilation fail, even a trivial script:
> {noformat}
> $ groovy -e 'println "x".toUpperCase()'
> SEVERE [groovy.ui.GroovyMain] Caught: {0}
> java.lang.NoClassDefFoundError: Unable to configure
> org.apache.groovy.groovyfx.extensions.FXExtensions due to missing dependency
> javafx/beans/binding/NumberBinding
> {noformat}
> Removing {{groovy-fx-*.jar}} from {{lib}} makes the error go away.
> h3. Cause
> The module descriptor registers {{FXExtensions}}, whose method signatures
> mention JavaFX types. The runtime already tolerates an extension class it
> cannot link: {{SimpleExtensionModule.getMetaMethods}} catches
> {{LinkageError}} per class and logs a warning. The compile-time cache does
> not. {{AbstractExtensionMethodCache.scan}}, which backs both static type
> checking and the macro transform, builds a {{ClassNode}} for every extension
> class visible to the loader. When one class cannot be configured, the error
> escapes and stops the compilation. The macro transform is a global transform,
> so this happens for every script, not just ones that use {{@CompileStatic}}.
> GroovyFX is the first module we ship whose descriptor-registered extension
> class depends on a compile-only dependency. A third-party extension module
> with an optional dependency fails in exactly the same way.
> h3. Proposed fix
> # *Backstop:* make {{AbstractExtensionMethodCache.scan}} catch
> {{LinkageError}} per extension class and log a warning that names the class,
> matching the runtime. A single unusable module can then no longer stop all
> compilation.
> # *Declared prerequisites:* add an optional {{requiredClasses}} key to the
> extension module descriptor. If any listed class cannot be loaded from the
> loader that defines the module's extension classes, the module is deferred
> instead of loaded. It is not registered, a FINE message reports the deferral,
> and its descriptor is remembered per class loader.
> # *Late activation:* after each {{@Grab}}, Grape (Ivy and Maven) re-checks
> the deferred modules. It registers any whose required classes are now
> loadable, using the existing GROOVY-5543 metaclass refresh. It then
> invalidates the compile-time extension method caches so static type checking
> and macros see the new methods. Grape does not invalidate those caches today,
> even for an ordinary grabbed extension module.
> # groovy-fx declares {{requiredClasses=javafx.beans.binding.NumberBinding}}
> through the build-logic {{moduleDescriptor}} DSL.
> Users who supply JavaFX through a build file, a JavaFX-bundling JDK or
> {{--module-path}} are unaffected: the probe succeeds at startup and the
> module registers as before.
> h3. Class loader caveat
> A jar in {{lib}} is defined by the launcher's {{RootLoader}}, so its classes
> can only link against classes that loader can see. A plain {{@Grab}} adds
> JavaFX to the script's child {{GroovyClassLoader}}, which the module cannot
> use. In that case the module stays deferred and a warning suggests
> {{@GrabConfig(systemClassLoader=true)}}, which works end to end. The same
> rule already applies to {{SceneGraphBuilder}} and every other class in the
> jar. The GroovyFX Grape recipe in the user guide should use
> {{systemClassLoader=true}}.
> h3. Verified with a prototype
> * A new forked-JVM test, {{DeferredExtensionModuleTest}}, covers four cases.
> A module is deferred, then activates for dynamic and {{@CompileStatic}} code.
> A module with its required class present loads normally. A module stays
> deferred when the class is visible only to a child loader. A module with an
> unlinkable signature no longer breaks compilation. That last case fails on
> master with the error above.
> * On an installed build that has groovy-fx in {{lib}} and no JavaFX, plain
> and {{@CompileStatic}} scripts run.
> * With {{@GrabConfig(systemClassLoader=true)}} and {{@Grab}} of
> {{javafx-base}}, the module activates, and {{SimpleIntegerProperty +
> SimpleIntegerProperty}} works both dynamically and under {{@CompileStatic}}.
> * The existing extension-module, {{DgmMethodLookupTest}}, Grape, groovy-macro
> and groovy-fx tests still pass.
> h3. Notes
> * The change adds new public API, all {{@since 7.0.0}}: the descriptor key
> constant, two static methods on {{ExtensionModuleScanner}}, and
> {{AbstractExtensionMethodCache.invalidateAllCaches()}}. The scanner methods
> could be made internal instead.
> * The descriptor key is additive. Older Groovy versions ignore it.
> * A simpler stopgap for 7.0.0-alpha-1 is to exclude groovy-fx from the
> distribution's {{lib}}. That stops the breakage without the new mechanism.
> * Separate cosmetic bug seen above: {{GroovyMain}} logs {{"Caught: {0}"}}
> without formatting, so the literal {{{0}}} is shown.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)