codeconsole commented on code in PR #16385:
URL: https://github.com/apache/grails-core/pull/16385#discussion_r4086806229
##########
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/scaffolding/GenerateScaffoldedViewsTask.groovy:
##########
@@ -237,6 +284,33 @@ abstract class GenerateScaffoldedViewsTask extends
DefaultTask {
found
}
+ /** Read declarations, including inherited ones, without evaluating
application code. */
+ private boolean hasNamespace(ClassReader reader, ClassLoader resources) {
+ boolean declared = false
+ reader.accept(new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ FieldVisitor visitField(int access, String name, String
descriptor, String signature, Object value) {
+ if (name == 'namespace' && (access & Opcodes.ACC_STATIC) != 0)
{
+ declared = true
+ }
+ null
+ }
+
+ @Override
+ MethodVisitor visitMethod(int access, String name, String
descriptor, String signature, String[] exceptions) {
+ if (name == 'getNamespace' && descriptor.startsWith('()') &&
(access & Opcodes.ACC_STATIC) != 0) {
+ declared = true
+ }
+ null
+ }
+ }, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG |
ClassReader.SKIP_FRAMES)
+ if (declared || reader.superName == null || reader.superName ==
'java/lang/Object') {
+ return declared
+ }
+ InputStream parent =
resources.getResourceAsStream("${reader.superName}.class")
+ parent == null ? false : parent.withCloseable { InputStream input ->
hasNamespace(new ClassReader(input), resources) }
Review Comment:
Both fixed in 68af07731d. A superclass whose class file the bundled ASM
rejects (`IllegalArgumentException`) is logged at info with its name and
treated as declaring no namespace, so the build continues. Superclass results
are memoized per run in a `Map<String, Boolean>` keyed on the internal name.
Specs cover an unreadable base in a dependency jar and several controllers
sharing one namespaced base.
##########
grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/views/gsp/GroovyPagePluginFunctionalSpec.groovy:
##########
@@ -128,4 +128,83 @@ class GroovyPagePluginFunctionalSpec extends
GradleSpecification {
and: 'so its test task is not put behind compiling pages it does not
read'
result.output.contains('TEST_WAITS_FOR_PAGE_COMPILATION=false')
}
+
+ def "staged scaffold views preserve namespaces and runtime plugin pages"()
{
+ given:
+ def runner = setupTestResourceProject('gsp-compile-classpath')
+ File projectDir = runner.projectDir
+ new File(projectDir, 'build.gradle').append("""
+ dependencies {
+ implementation localGroovy()
+ runtimeOnly files('calendar-plugin')
+ }
+ sourceSets.main.groovy.srcDir('grails-app/controllers')
+ """)
+ // Only the annotation's bytecode is consumed by the task; no
application is started.
+ Map<String, String> sources = [
+
'src/main/groovy/grails/plugin/scaffolding/annotation/Scaffold.groovy': '''
+ package grails.plugin.scaffolding.annotation
+ import java.lang.annotation.Retention
+ import java.lang.annotation.RetentionPolicy
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Scaffold { Class value() }
+ ''',
+ 'grails-app/controllers/admin/EventController.groovy': '''
+ package admin
+ import grails.plugin.scaffolding.annotation.Scaffold
+ @Scaffold(String)
+ class EventController { static namespace = 'admin' }
+ ''',
+ 'grails-app/controllers/admin/DashboardController.groovy': '''
+ package admin
+ class DashboardController { static namespace = 'admin' }
+ ''',
+ 'grails-app/controllers/PersonController.groovy': '''
+ import grails.plugin.scaffolding.annotation.Scaffold
+ @Scaffold(String)
+ class PersonController { }
+ ''',
+ 'grails-app/controllers/BookController.groovy': '''
+ import grails.plugin.scaffolding.annotation.Scaffold
+ @Scaffold(String)
+ class BookController { }
+ ''',
+ 'src/main/templates/scaffolding/show.gsp': 'scaffold ${className}',
+ 'grails-app/views/person/index.gsp': 'handwritten index',
+ 'calendar-plugin/gsp/views.properties': '''
+ /WEB-INF/grails-app/views/event/show.gsp=calendar_event_show
+ /WEB-INF/grails-app/views/person/show.gsp=calendar_person_show
+ '''
+ ]
+ sources.each { String path, String content ->
+ File file = new File(projectDir, path)
+ file.parentFile.mkdirs()
+ file.text = content.stripIndent()
+ }
+
+ when:
+ def result = executeTask('stageGroovyPages')
+ File staged = new File(projectDir, 'build/generated/views')
+
+ then: 'the GSP compiler never receives an application page that would
shadow a plugin'
+ assertTaskSuccess('stageGroovyPages', result)
+ !new File(staged, 'event/show.gsp').exists()
+ !new File(staged, 'person/show.gsp').exists()
+ new File(staged, 'book/show.gsp').text == 'scaffold String'
+ new File(staged, 'person/index.gsp').text == 'handwritten index'
+
+ and: 'only skipped scaffold views warn about the native-image
requirement'
+ result.output.contains('Not precompiling the views of event:')
+ result.output.contains('native images require concrete GSP views')
+ !result.output.contains('Not precompiling the views of dashboard:')
+
+ when: 'a runtime dependency no longer provides a page, invalidating
the generation task'
+ new File(projectDir, 'calendar-plugin/gsp/views.properties').text = ''
+ executeTask('stageGroovyPages')
Review Comment:
Fixed in 9d50cf5faa. The incremental phase now asserts
`assertTaskSuccess('stageGroovyPages', rebuild)`, which requires `SUCCESS`, so
an unexpected UP-TO-DATE fails there.
--
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]