You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/10/16 16:36:57 UTC
(grails-core) 01/02: fix(forge): keep the Grails Data plugin off the generated buildscript classpath
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch fix/forge-grails-data-buildscript-classpath
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 870f3353f5483934e2ac6e0b417402407d906b6d
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sat Oct 10 11:42:54 2026 -0500
fix(forge): keep the Grails Data plugin off the generated buildscript classpath
Generated builds declared grails-data-hibernate5/7 as a buildscript classpath
entry (and a buildSrc implementation dependency), a leftover from when the CLI
found plugin commands through the build classpath; commands now ship in the
companion -cli artifacts the Grails Gradle plugin discovers from the
application's dependencies. The plugin pulls in grails-datamapping-core, which
registers global AST transformations, so Gradle's embedded Groovy 4 ran them
while compiling build.gradle. With the transformations now compiled by Groovy
6 that fails at configuration time (NoSuchMethodError NumberMath.remainder,
Build Grails Forge job: CreateDomainClassCommandSpec and the other command
specs); with the Java-compiled transformations it merely ran them for nothing.
The plugin stays an application dependency only. The upgrade guide tells
existing builds to drop the classpath entry and names the symptom.
Co-Authored-By: Claude Sonnet 5.5 <no...@anthropic.com>
(cherry picked from commit 7322d8190f8623ac3a340fddab1212123fe618fb)
---
grails-doc/src/en/guide/introduction/whatsNew.adoc | 3 ++
.../src/en/guide/upgrading/upgrading80x.adoc | 36 ++++++++++++++++++++++
.../feature/database/GrailsDataHibernate5.java | 7 ++---
.../feature/database/GrailsDataHibernate7.java | 7 ++---
.../database/GrailsDataHibernate5Spec.groovy | 11 ++++---
.../database/GrailsDataHibernate7Spec.groovy | 10 +++---
6 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/grails-doc/src/en/guide/introduction/whatsNew.adoc b/grails-doc/src/en/guide/introduction/whatsNew.adoc
index daf9204071..e39305da4e 100644
--- a/grails-doc/src/en/guide/introduction/whatsNew.adoc
+++ b/grails-doc/src/en/guide/introduction/whatsNew.adoc
@@ -73,6 +73,9 @@ Applications generated for Hibernate 7 consume `grails-hibernate7-bom` consisten
This avoids the dependency resolution conflicts that occur when an application generated for Hibernate 5 is manually switched to `grails-data-hibernate7` while the default `grails-bom` remains on the build classpath.
The `database-migration` feature also selects the matching `grails-data-hibernate7-dbmigration` plugin automatically.
+The Grails Data plugin itself (`grails-data-hibernate5` or `grails-data-hibernate7`) is an application dependency only: generated builds no longer place it on the `buildscript` classpath or in `buildSrc`, where Gradle's own Groovy would run the compile-time transformations the plugin carries while compiling the build scripts.
+See the xref:upgrading#upgrading80x[upgrade guide] for the equivalent change in an existing build.
+
==== Asynchronous Grails Data Application Generation
Grails Forge offers a new `gorm-async` feature that adds the asynchronous programming model for Grails Data — the `AsyncEntity` trait and the `async` namespace — to a generated application:
diff --git a/grails-doc/src/en/guide/upgrading/upgrading80x.adoc b/grails-doc/src/en/guide/upgrading/upgrading80x.adoc
index 5935fba7b8..417530f179 100644
--- a/grails-doc/src/en/guide/upgrading/upgrading80x.adoc
+++ b/grails-doc/src/en/guide/upgrading/upgrading80x.adoc
@@ -4779,3 +4779,39 @@ When upgrading from an earlier Grails 8 milestone or release candidate, update t
With the configuration cache, task actions such as `doFirst` and `doLast` closures may not use `project`, other tasks or build script properties while they run, and Gradle fails the build when a build script or another Gradle plugin does.
See xref:commandLine#gradleConfigurationCache[Using the Gradle Configuration Cache] for how to adapt build scripts and what to do about plugins that do not support it.
+
+==== 86. Grails Data Plugins Leave the Buildscript Classpath
+
+Builds generated by earlier versions of Grails Forge placed the Grails Data plugin on the Gradle `buildscript` classpath, next to the Grails Gradle plugins, and in `buildSrc` when the build used one:
+
+[source,groovy]
+.build.gradle (before)
+----
+buildscript {
+ dependencies {
+ classpath "org.apache.grails:grails-gradle-plugins"
+ classpath "org.apache.grails:grails-data-hibernate5"
+ }
+}
+----
+
+Remove that `classpath` entry (and the matching `implementation` line in `buildSrc/build.gradle`), keeping only the `implementation` dependency of the application itself:
+
+[source,groovy]
+.build.gradle (after)
+----
+buildscript {
+ dependencies {
+ classpath "org.apache.grails:grails-gradle-plugins"
+ }
+}
+
+dependencies {
+ implementation "org.apache.grails:grails-data-hibernate5"
+}
+----
+
+The entry was a leftover from the days when the Grails CLI found a plugin's commands through the build classpath; commands now ship in companion `-cli` artifacts that the Grails Gradle plugin discovers from the application's dependencies (see <<_cli_commands_move_to_companion_cli_artifacts,CLI Commands Move to Companion `-cli` Artifacts>>).
+Left in place, the plugin and its transitive Grails Data modules are loaded by the Groovy that Gradle embeds while it compiles `build.gradle`, and the global compile-time transformations those modules register run in that older Groovy.
+A build whose `buildscript` classpath still carries the plugin can fail at configuration time with an error such as `java.lang.NoSuchMethodError: 'java.lang.Number org.codehaus.groovy.runtime.typehandling.NumberMath.remainder(java.lang.Number, java.lang.Number)'` raised from `GlobalDetachedCriteriaASTTransformation`.
+The same applies to `grails-data-hibernate7` and to any other Grails module that registers a global transformation: none of them belong on the `buildscript` classpath.
diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate5.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate5.java
index 7b8ebf8400..a13c8c4441 100644
--- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate5.java
+++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate5.java
@@ -81,10 +81,9 @@ public class GrailsDataHibernate5 extends GormFeature implements DatabaseDriverC
config.put("hibernate.cache.use_second_level_cache", false);
config.put("hibernate.cache.use_query_cache", false);
- generatorContext.addBuildscriptDependency(Dependency.builder()
- .groupId("org.apache.grails")
- .artifactId("grails-data-hibernate5")
- .buildSrc());
+ // runtime only: the plugin jar must stay off the buildscript classpath, where Gradle's own
+ // Groovy would run the global AST transformations it carries while compiling build.gradle;
+ // its commands ship in the companion -cli artifact that the Grails Gradle plugin discovers
generatorContext.addDependency(Dependency.builder()
.groupId("org.apache.grails")
.artifactId("grails-data-hibernate5")
diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate7.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate7.java
index 4fb30ce28b..175fc30fe4 100644
--- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate7.java
+++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/database/GrailsDataHibernate7.java
@@ -79,10 +79,9 @@ public class GrailsDataHibernate7 extends GormFeature implements DatabaseDriverC
config.put("hibernate.cache.use_second_level_cache", false);
config.put("hibernate.cache.use_query_cache", false);
- generatorContext.addBuildscriptDependency(Dependency.builder()
- .groupId("org.apache.grails")
- .artifactId("grails-data-hibernate7")
- .buildSrc());
+ // runtime only: the plugin jar must stay off the buildscript classpath, where Gradle's own
+ // Groovy would run the global AST transformations it carries while compiling build.gradle;
+ // its commands ship in the companion -cli artifact that the Grails Gradle plugin discovers
generatorContext.addDependency(Dependency.builder()
.groupId("org.apache.grails")
.artifactId("grails-data-hibernate7")
diff --git a/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate5Spec.groovy b/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate5Spec.groovy
index 7aae8c4a2d..298e179f81 100644
--- a/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate5Spec.groovy
+++ b/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate5Spec.groovy
@@ -54,25 +54,26 @@ class GrailsDataHibernate5Spec extends ApplicationContextSpec implements Command
template.contains('runtimeOnly "com.h2database:h2"')
}
- void "test dependencies are present for buildSrc"() {
+ void "test the plugin is kept out of buildSrc"() {
when:
final String template = new BuildBuilder(beanContext)
.features(["gorm-hibernate5"])
.renderBuildSrc()
then:
- template.contains('implementation "org.apache.grails:grails-data-hibernate5"')
+ !template.contains('org.apache.grails:grails-data-hibernate5')
}
- void "test buildSrc is present for buildscript dependencies"() {
+ void "test the plugin is kept off the buildscript classpath"() {
given:
final def output = generate(ApplicationType.WEB, new Options(DevelopmentReloading.DEVTOOLS))
final def buildGradle = output["build.gradle"]
expect:
buildGradle != null
- buildGradle.contains("classpath \"org.apache.grails:grails-data-hibernate5\"")
-
+ buildGradle.contains("classpath \"org.apache.grails:grails-gradle-plugins\"")
+ !buildGradle.contains("classpath \"org.apache.grails:grails-data-hibernate5\"")
+ buildGradle.contains("implementation \"org.apache.grails:grails-data-hibernate5\"")
}
void "test config"() {
diff --git a/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate7Spec.groovy b/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate7Spec.groovy
index d27b2e9e32..36ed48fd23 100644
--- a/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate7Spec.groovy
+++ b/grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/database/GrailsDataHibernate7Spec.groovy
@@ -147,7 +147,7 @@ class GrailsDataHibernate7Spec extends ApplicationContextSpec implements Command
!template.contains('grails-hibernate7-micronaut-bom')
}
- void "test dependencies are present for buildSrc"() {
+ void "test the Hibernate 7 BOM is used for buildSrc and the plugin is kept out of it"() {
when:
final String template = new BuildBuilder(beanContext)
.features(["gorm-hibernate7"])
@@ -155,19 +155,21 @@ class GrailsDataHibernate7Spec extends ApplicationContextSpec implements Command
then:
template.contains('implementation platform("org.apache.grails:grails-hibernate7-bom:$grailsVersion")')
- template.contains('implementation "org.apache.grails:grails-data-hibernate7"')
+ !template.contains('org.apache.grails:grails-data-hibernate7')
!template.contains('org.apache.grails:grails-bom')
!template.contains('grails-data-hibernate5')
}
- void "test buildSrc is present for buildscript dependencies"() {
+ void "test the plugin is kept off the buildscript classpath"() {
given:
final def output = generate(ApplicationType.WEB, hibernate7Options())
final def buildGradle = output["build.gradle"]
expect:
buildGradle != null
- buildGradle.contains("classpath \"org.apache.grails:grails-data-hibernate7\"")
+ buildGradle.contains("classpath \"org.apache.grails:grails-gradle-plugins\"")
+ !buildGradle.contains("classpath \"org.apache.grails:grails-data-hibernate7\"")
+ buildGradle.contains("implementation \"org.apache.grails:grails-data-hibernate7\"")
!buildGradle.contains("grails-data-hibernate5")
}