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

   Follow-up to #16398.
   
   `generateScaffoldedViews` reads scaffolded controllers from every plugin on 
the **runtime** classpath, but `compileGroovyPages` compiles the pages it 
expands against a classpath built from the **compile** classpath. A plugin that 
reaches a project only at runtime is on the first and not the second. The usual 
way that happens is an `implementation` dependency of a project this one 
depends on. Every page expanded for such a plugin's controllers names a domain 
class the page compiler cannot resolve, so the compiler leaves each one out 
with a warning and the compiler error (names changed):
   
   ```text
   Left out the optional page 
grails-scaffolded/com.example.images.ManagedFile/index-78a2d25e9f37d1c1.gsp, 
which does not compile; if it is rendered it is produced then instead, and 
fails the same way: startup failed:
   
gsp_reports_grails_scaffolded_com_example_images_ManagedFileindex_78a2d25e9f37d1c1_gsp.groovy:
 13: unable to resolve class com.example.images.ManagedFile
   ```
   
   Found in an application whose plugins depend on one another. A plugin that 
uses another plugin's services through an `implementation` dependency got the 
other plugin's scaffolded controllers on its runtime classpath. It then 
expanded and failed to compile four pages for each of them. That repeats in 
every project the plugin reaches, while the plugin's own build, which does have 
the domain classes, expands and compiles the same pages anyway. No page is lost 
by skipping them; the only effect was the warnings.
   
   - `GenerateScaffoldedViewsTask` gains `pageClasspath`: the classpath the 
pages are compiled against (`@CompileClasspath`). A plugin's controller has its 
pages expanded only where the domain class it scaffolds is on it. A controller 
left out is noted at INFO, naming the controller and the domain class.
   - `GroovyPagePlugin` wires `pageClasspath` from the same `FileCollection` it 
gives `compileGroovyPages`, so the check and the compilation cannot disagree. 
The consequence is that `generateScaffoldedViews` now also waits for 
`packageTagLibraryIndex`, which is part of that collection.
   - The class loader for the check has no parent, but a `URLClassLoader` with 
a `null` parent still delegates to the bootstrap loader. So a domain type the 
platform provides, such as `java.lang.Long`, resolves without an entry.
   - An application's own controllers are not checked. They were compiled 
against their domain classes, so those classes are on the compile classpath by 
construction.
   - The scaffolding guide says which plugins are covered. For a native image, 
the fix for a plugin an application has only at runtime is to declare it as a 
dependency of the application.
   
   Why this rather than compiling the pages against the runtime classpath: the 
generated pages and the application's views share one compilation (and one 
`gsp/views.properties`). Adding the runtime classpath there would also change 
what hand-written views compile against, and which plugins' tag libraries they 
resolve statically.
   
   A task registered by hand without `pageClasspath` expands pages for the 
application's own controllers as before. Only plugin controllers whose domain 
class is not a platform type are affected, and those are left out at INFO.
   
   Validation, from `grails-gradle/`:
   
   ```text
   ./gradlew :grails-gradle-plugins:test --tests 
'org.grails.gradle.plugin.scaffolding.*' --tests 
'org.grails.gradle.plugin.views.gsp.*'
   ./gradlew :grails-gradle-plugins:codeStyle
   ```
   
   All pass: 78 tests across `GenerateScaffoldedViewsTaskSpec` (34), 
`GroovyPagePluginFunctionalSpec` (10), `TagLibraryIndexWiringFunctionalSpec` 
(7, which includes the no-dependency-cycle and configuration-cache wiring 
checks), `GenerateTagLibraryIndexTaskSpec` (15), `TagLibraryIndexFilesSpec` (7) 
and `GroovyPageToolchainSpec` (5).
   
   Coverage added:
   
   - `GroovyPagePluginFunctionalSpec`: a TestKit build with one plugin as an 
`implementation` dependency and one as `runtimeOnly`, each carrying its domain 
class. The first has its pages generated and handed to the compiler. The second 
has none, and the INFO note names it.
   - `GenerateScaffoldedViewsTaskSpec`: a plugin whose domain class is not on 
`pageClasspath` is left out while the others are expanded. A platform-provided 
domain type, and a plugin controller scaffolding a domain class of the 
project's own, are still expanded. The two existing plugin tests now put the 
plugin on `pageClasspath`, as an `implementation` dependency would.
   
   With the main-source change reverted, the new functional test and the new 
left-out unit test both fail. The two updated plugin tests fail too, because 
the property does not exist without the change.
   
   Not run, and **not claimed to pass**: the full `grails-gradle-plugins` test 
suite and the repository-wide `aggregateViolations` check.
   


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