@@ -32,6 +32,7 @@ import org.gradle.api.artifacts.DependencyConstraint
3232import org.gradle.api.artifacts.ExcludeRule
3333import org.gradle.api.artifacts.ModuleDependency
3434import org.gradle.api.artifacts.component.ModuleComponentSelector
35+ import org.gradle.api.artifacts.component.ProjectComponentSelector
3536import org.gradle.api.artifacts.result.DependencyResult
3637import org.gradle.api.artifacts.result.ResolvedDependencyResult
3738import org.gradle.api.file.ConfigurableFileCollection
@@ -83,12 +84,28 @@ abstract class ExtractDependenciesTask extends DefaultTask {
8384 @Input
8485 abstract MapProperty<String , String > getProjectCoordinateProperties ()
8586
87+ /**
88+ * When {@code true }, transitive platform dependencies that are not explicitly
89+ * registered in {@code combinedPlatforms } / {@code dependencies.gradle } will be
90+ * auto-registered in {@link PropertyNameCalculator} instead of causing a build
91+ * failure. This is required for BOMs that import an external platform via the
92+ * gradle module format (e.g. {@code micronaut-platform }) which itself imports
93+ * many sub-BOMs that Grails does not manage directly.
94+ *
95+ * <p >Defaults to {@code false } so that existing BOMs (grails-bom, grails-base-bom)
96+ * retain the strict validation that every platform dependency has a known property
97+ * name in {@code dependencies.gradle }.</p>
98+ */
99+ @Input
100+ abstract Property<Boolean > getAutoRegisterTransitivePlatforms ()
101+
86102 void setConfiguration (NamedDomainObjectProvider<Configuration > config ) {
87103 dependencyArtifacts. from(config)
88104 configurationName. set(config. name)
89105 }
90106
91107 ExtractDependenciesTask () {
108+ autoRegisterTransitivePlatforms. convention(false )
92109 doFirst {
93110 if (! project. pluginManager. hasPlugin(' java-platform' )) {
94111 throw new GradleException (/ The 'java-platform' plugin must be applied to the project to use this task./ )
@@ -188,6 +205,13 @@ abstract class ExtractDependenciesTask extends DefaultTask {
188205 }
189206
190207 ResolvedDependencyResult dep = (ResolvedDependencyResult ) result
208+
209+ // Skip project dependencies (e.g. platform(project(':grails-bom'))) since their
210+ // constraints are already captured through the explicit constraints population
211+ if (dep. requested instanceof ProjectComponentSelector ) {
212+ continue
213+ }
214+
191215 ModuleComponentSelector moduleComponentSelector = dep. requested as ModuleComponentSelector
192216
193217 // Any non-constraint via api dependency should *always* be a platform dependency, so expand each of those
@@ -199,6 +223,16 @@ abstract class ExtractDependenciesTask extends DefaultTask {
199223
200224 // fetch the BOM as a pom file so it can be expanded
201225 ExtractedDependencyConstraint constraint = propertyNameCalculator. calculate(bomCoordinate. groupId, bomCoordinate. artifactId, bomCoordinate. version, true )
226+ if (! constraint) {
227+ if (autoRegisterTransitivePlatforms. get()) {
228+ // Auto-register the transitive platform so it can be documented and expanded,
229+ // but without a version property reference since the version is managed by the
230+ // parent platform (e.g. micronaut-platform), not by a Grails property.
231+ constraint = new ExtractedDependencyConstraint (bomCoordinate. coordinates)
232+ } else {
233+ throw new GradleException (" Failed to find a property name for BOM dependency: ${ bomCoordinate.coordinates} . All platform dependencies must have a property name defined meeting the naming requirements." )
234+ }
235+ }
202236 constraint. source = bomCoordinate. artifactId
203237 constraints. put(bomCoordinate. toCoordinateHolder(), constraint)
204238
0 commit comments