You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ma...@apache.org on 2026/09/08 18:59:01 UTC
(grails-core) branch test/compiler-config-runtime-classpath-producer created (now 9cde802ef7)
This is an automated email from the ASF dual-hosted git repository.
matrei pushed a change to branch test/compiler-config-runtime-classpath-producer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
at 9cde802ef7 test: pin that the compiler config generator stays off the runtime classpath
This branch includes the following new commits:
new 9cde802ef7 test: pin that the compiler config generator stays off the runtime classpath
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(grails-core) 01/01: test: pin that the compiler config generator stays off the runtime classpath
Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
matrei pushed a commit to branch test/compiler-config-runtime-classpath-producer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 9cde802ef7f9aff0bc4d65fc2872e294f38e9911
Author: Mattias Reichel <ma...@gmail.com>
AuthorDate: Tue Sep 8 20:53:37 2026 +0200
test: pin that the compiler config generator stays off the runtime classpath
The task-based wiring that 9a529d1996 replaced with a doFirst declared
runtimeClasspath as an input of the generator task, so any task whose
output reached the runtime classpath became a transitive dependency of
compileGroovy. A post-compile step that feeds the runtime classpath, such
as asset-pipeline output or the exploded plugin directory, then closed a
cycle. That is the concern raised on #16114.
The generator introduced there takes its script from grails { } state
and declares no classpath input, so that edge no longer exists. This
test recreates the scenario: a task that runs after classes and puts its
output on the runtime classpath via runtimeOnly files(task). It asserts
the generator does not depend on that task, and that compileGroovy and
the producer schedule together without a cycle.
---
.../core/GrailsGroovyCompilerConfigSpec.groovy | 23 +++++++++++++
.../build.gradle | 40 ++++++++++++++++++++++
.../gradle.properties | 1 +
.../settings.gradle | 1 +
4 files changed, 65 insertions(+)
diff --git a/grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/core/GrailsGroovyCompilerConfigSpec.groovy b/grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/core/GrailsGroovyCompilerConfigSpec.groovy
index 184e11bb35..14a11383f7 100644
--- a/grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/core/GrailsGroovyCompilerConfigSpec.groovy
+++ b/grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/core/GrailsGroovyCompilerConfigSpec.groovy
@@ -59,6 +59,29 @@ class GrailsGroovyCompilerConfigSpec extends GradleSpecification {
result.output.contains('GENERATOR_HAS_CONTENT_INPUT=true')
}
+ def "a task that adds to the runtime classpath after compiling does not cycle through the generator"() {
+ given: 'an application whose runtime classpath carries the output of a task that runs after classes'
+ setupTestResourceProject('compiler-config-runtime-classpath-producer')
+
+ when: 'the wiring is inspected'
+ def result = executeTask('inspectRuntimeProducer')
+
+ then: 'the producer is on the runtime classpath, and the generator does not depend on it'
+ result.output.contains('RUNTIME_CLASSPATH_BUILT_BY_PRODUCER=true')
+ result.output.contains('GENERATOR_DEPENDS_ON_RUNTIME_PRODUCER=false')
+ result.output.contains('COMBINED_CONTAINS_GRAILS_IMPORT=true')
+
+ when: 'the compile task and that producer are scheduled together'
+ def graph = executeTask('compileGroovy', ['packageRuntimeExtra', '--dry-run'])
+
+ then: 'the graph builds without a cycle, the generator ahead of the compile and the producer after'
+ def order = graph.output.readLines().findAll { it.startsWith(':') }
+ order.findIndexOf { it.startsWith(':generateCompileGroovyGrailsCompilerConfig') } <
+ order.findIndexOf { it.startsWith(':compileGroovy') }
+ order.findIndexOf { it.startsWith(':compileGroovy') } <
+ order.findIndexOf { it.startsWith(':packageRuntimeExtra') }
+ }
+
def "a configurationScript assigned from a later callback is folded in, not clobbered"() {
given: 'a user assigning configurationScript from projectsEvaluated'
setupTestResourceProject('compiler-config-late-assignment')
diff --git a/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/build.gradle b/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/build.gradle
new file mode 100644
index 0000000000..9e51e9df1b
--- /dev/null
+++ b/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/build.gradle
@@ -0,0 +1,40 @@
+plugins {
+ id 'org.apache.grails.gradle.grails-app'
+}
+
+grails {
+ starImports = ['com.example.custom']
+}
+
+// A task that runs after the classes are compiled and puts its output on the runtime
+// classpath: the shape of asset-pipeline, the exploded plugin directory, or any other
+// post-compile packaging step. Its output reaches compileGroovy only through the
+// runtime classpath, which the compile classpath does not include.
+def packageRuntimeExtra = tasks.register('packageRuntimeExtra') {
+ dependsOn 'classes'
+ def out = layout.buildDirectory.dir('runtime-extra')
+ outputs.dir(out)
+ doLast {
+ def dir = out.get().asFile
+ dir.mkdirs()
+ new File(dir, 'extra.properties').text = 'generated=true'
+ }
+}
+
+dependencies {
+ runtimeOnly files(packageRuntimeExtra)
+}
+
+tasks.register('inspectRuntimeProducer') {
+ dependsOn 'generateCompileGroovyGrailsCompilerConfig'
+ doLast {
+ def gen = tasks.named('generateCompileGroovyGrailsCompilerConfig').get()
+ def deps = gen.taskDependencies.getDependencies(gen)*.path
+ println "GENERATOR_DEPENDENCIES=${deps}"
+ println "GENERATOR_DEPENDS_ON_RUNTIME_PRODUCER=${deps.any { it == ':packageRuntimeExtra' }}"
+ def runtimeProducers = configurations.runtimeClasspath.buildDependencies.getDependencies(null)*.path
+ println "RUNTIME_CLASSPATH_BUILT_BY_PRODUCER=${runtimeProducers.any { it == ':packageRuntimeExtra' }}"
+ def combined = file('build/grailsGroovyCompilerConfig-compileGroovy.groovy')
+ println "COMBINED_CONTAINS_GRAILS_IMPORT=${combined.exists() && combined.text.contains(/star 'com.example.custom'/)}"
+ }
+}
diff --git a/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/gradle.properties b/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/gradle.properties
new file mode 100644
index 0000000000..35c332fb87
--- /dev/null
+++ b/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/gradle.properties
@@ -0,0 +1 @@
+grailsVersion=__PROJECT_VERSION__
diff --git a/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/settings.gradle b/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/settings.gradle
new file mode 100644
index 0000000000..ce227a48f9
--- /dev/null
+++ b/grails-gradle/plugins/src/test/resources/test-projects/compiler-config-runtime-classpath-producer/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'compiler-config-runtime-classpath-producer'