This is an automated email from the ASF dual-hosted git repository.

jamesfredley pushed a commit to branch feat/automatic-module-names
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 7f4e24d9b0d60e876d9301ff02ddfcd111f1f46f
Author: James Fredley <[email protected]>
AuthorDate: Fri Jul 10 12:57:55 2026 -0400

    Add Automatic-Module-Name jar manifests
    
    Generate Automatic-Module-Name entries from build-logic for published 
modules.
    
    Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]
---
 .../apache/grails/buildsrc/CompilePlugin.groovy    | 16 ++++++++
 .../grails/buildsrc/CompilePluginSpec.groovy       | 43 ++++++++++++++++++++++
 grails-doc/src/en/guide/toc.yml                    |  1 +
 .../en/guide/upgrading/automaticModuleNames.adoc   |  7 ++++
 4 files changed, 67 insertions(+)

diff --git 
a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy
 
b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy
index b139dacf16..7253694e79 100644
--- 
a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy
+++ 
b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy
@@ -83,11 +83,27 @@ class CompilePlugin implements Plugin<Project> {
                     'Implementation-Version': lookupPropertyByType(project, 
'grailsVersion', String),
                     'Implementation-Vendor': 'grails.apache.org'
             )
+            if (!jar.archiveClassifier.present) {
+                jar.manifest.attributes('Automatic-Module-Name': 
automaticModuleName(project))
+            }
             // Explicitly fail since duplicates indicate a double 
configuration that needs fixed
             jar.duplicatesStrategy = DuplicatesStrategy.FAIL
         }
     }
 
+    static String automaticModuleName(Project project) {
+        String configuredModuleName = 
project.findProperty('automaticModuleName') as String
+        if (configuredModuleName) {
+            return configuredModuleName
+        }
+        String groupPrefix = project.group == null ? 'org.apache.grails' : 
project.group.toString()
+        return "${groupPrefix}.${project.name}"
+                .replace('-', '.')
+                .replaceAll('[^A-Za-z0-9_.]', '.')
+                .replaceAll('\\.+', '.')
+                .replaceAll('^\\.|\\.$', '')
+    }
+
     private static void configureCompiler(Project project) {
         project.tasks.withType(JavaCompile).configureEach {
             // Preserve method parameter names in Groovy/Java classes for IDE 
parameter hints & bean reflection metadata.
diff --git 
a/build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/CompilePluginSpec.groovy
 
b/build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/CompilePluginSpec.groovy
new file mode 100644
index 0000000000..3eb7b83888
--- /dev/null
+++ 
b/build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/CompilePluginSpec.groovy
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.grails.buildsrc
+
+import org.gradle.testfixtures.ProjectBuilder
+import spock.lang.Specification
+
+class CompilePluginSpec extends Specification {
+
+    void 'automatic module name is derived from group and project name'() {
+        given:
+        def project = 
ProjectBuilder.builder().withName('grails-web-common').build()
+        project.group = 'org.apache.grails.web'
+
+        expect:
+        CompilePlugin.automaticModuleName(project) == 
'org.apache.grails.web.grails.web.common'
+    }
+
+    void 'automatic module name can be overridden per project'() {
+        given:
+        def project = 
ProjectBuilder.builder().withName('grails-web-common').build()
+        project.ext.automaticModuleName = 'org.apache.grails.web.common'
+
+        expect:
+        CompilePlugin.automaticModuleName(project) == 
'org.apache.grails.web.common'
+    }
+}
diff --git a/grails-doc/src/en/guide/toc.yml b/grails-doc/src/en/guide/toc.yml
index b0e4a5c3ff..2aa8032711 100644
--- a/grails-doc/src/en/guide/toc.yml
+++ b/grails-doc/src/en/guide/toc.yml
@@ -37,6 +37,7 @@ gettingStarted:
 upgrading:
   title: Upgrading from the previous versions
   upgrading80x: Upgrading from Grails 7 to Grails 8
+  automaticModuleNames: Automatic module names
   upgrading72x: Upgrading from Grails 7.1 to Grails 7.2
   upgrading71x: Upgrading from Grails 7.0 to Grails 7.1
   upgrading70x: Upgrading from Grails 6 to Grails 7.0
diff --git a/grails-doc/src/en/guide/upgrading/automaticModuleNames.adoc 
b/grails-doc/src/en/guide/upgrading/automaticModuleNames.adoc
new file mode 100644
index 0000000000..c2d9906e61
--- /dev/null
+++ b/grails-doc/src/en/guide/upgrading/automaticModuleNames.adoc
@@ -0,0 +1,7 @@
+=== Automatic module names
+
+Grails 8.1 starts publishing `Automatic-Module-Name` manifest entries for the 
main framework jars produced by the shared compile convention.
+By default the module name is derived from the Gradle project group and 
project name, replacing hyphens with dots.
+
+Examples include `org.apache.grails.grails.core` for `grails-core` and 
`org.apache.grails.web.grails.web.common` for `grails-web-common`.
+Individual modules may set an `automaticModuleName` project property when a 
shorter stable JPMS name is agreed for a follow-up slice.

Reply via email to