From cb595661650368892dc7c9260554a4351fb94be2 Mon Sep 17 00:00:00 2001 From: Tim Hurman Date: Wed, 5 Nov 2025 00:33:27 +0900 Subject: [PATCH 1/2] dded the abilityt o set a platform dependency for version enforcement. --- .../ExtraJavaModuleInfoPluginExtension.java | 3 ++ .../moduleinfo/PublishedMetadata.java | 50 ++++++++++++++++--- .../test/EdgeCasesFunctionalTest.groovy | 21 ++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gradlex/javamodule/moduleinfo/ExtraJavaModuleInfoPluginExtension.java b/src/main/java/org/gradlex/javamodule/moduleinfo/ExtraJavaModuleInfoPluginExtension.java index 19f2e1f..e994270 100644 --- a/src/main/java/org/gradlex/javamodule/moduleinfo/ExtraJavaModuleInfoPluginExtension.java +++ b/src/main/java/org/gradlex/javamodule/moduleinfo/ExtraJavaModuleInfoPluginExtension.java @@ -7,6 +7,7 @@ import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.MinimalExternalModuleDependency; import org.gradle.api.attributes.Attribute; import org.gradle.api.model.ObjectFactory; @@ -44,6 +45,8 @@ public abstract class ExtraJavaModuleInfoPluginExtension { public abstract Property getVersionsProvidingConfiguration(); + public abstract Property getPlatformDependency(); + /** * Add full module information for a given Jar file. * diff --git a/src/main/java/org/gradlex/javamodule/moduleinfo/PublishedMetadata.java b/src/main/java/org/gradlex/javamodule/moduleinfo/PublishedMetadata.java index 135dd6a..f6a7903 100644 --- a/src/main/java/org/gradlex/javamodule/moduleinfo/PublishedMetadata.java +++ b/src/main/java/org/gradlex/javamodule/moduleinfo/PublishedMetadata.java @@ -4,7 +4,9 @@ import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE; +import static org.gradle.api.attributes.Category.ENFORCED_PLATFORM; import static org.gradle.api.attributes.Category.LIBRARY; +import static org.gradle.api.attributes.Category.REGULAR_PLATFORM; import static org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE; import java.io.Serializable; @@ -15,6 +17,8 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ModuleDependency; import org.gradle.api.artifacts.result.DependencyResult; import org.gradle.api.artifacts.result.ResolvedDependencyResult; import org.gradle.api.artifacts.result.UnresolvedDependencyResult; @@ -34,6 +38,8 @@ public class PublishedMetadata implements Serializable { private static final Attribute CATEGORY_ATTRIBUTE_UNTYPED = Attribute.of(CATEGORY_ATTRIBUTE.getName(), String.class); + private static final Attribute CATEGORY_ATTRIBUTE_TYPED = + Attribute.of(CATEGORY_ATTRIBUTE.getName(), Category.class); private static final String DEFAULT_VERSION_SOURCE_CONFIGURATION = "definedDependenciesVersions"; private final String gav; @@ -47,10 +53,16 @@ public class PublishedMetadata implements Serializable { PublishedMetadata(String gav, Project project, ExtraJavaModuleInfoPluginExtension extension) { this.gav = gav; - List compileDependencies = - componentVariant(extension.getVersionsProvidingConfiguration(), project, Usage.JAVA_API); - List runtimeDependencies = - componentVariant(extension.getVersionsProvidingConfiguration(), project, Usage.JAVA_RUNTIME); + List compileDependencies = componentVariant( + extension.getVersionsProvidingConfiguration(), + extension.getPlatformDependency(), + project, + Usage.JAVA_API); + List runtimeDependencies = componentVariant( + extension.getVersionsProvidingConfiguration(), + extension.getPlatformDependency(), + project, + Usage.JAVA_RUNTIME); Stream.concat(compileDependencies.stream(), runtimeDependencies.stream()) .distinct() @@ -67,7 +79,10 @@ public class PublishedMetadata implements Serializable { @SuppressWarnings({"UnstableApiUsage", "unchecked"}) private List componentVariant( - Provider versionsProvidingConfiguration, Project project, String usage) { + Provider versionsProvidingConfiguration, + Provider platformDependencyProvider, + Project project, + String usage) { Configuration versionsSource; if (versionsProvidingConfiguration.isPresent()) { versionsSource = project.getConfigurations() @@ -81,8 +96,29 @@ private List componentVariant( project.getExtensions().findByType(SourceSetContainer.class)); } - Configuration singleComponentVariantResolver = project.getConfigurations() - .detachedConfiguration(project.getDependencies().create(gav)); + List dependencies = new ArrayList<>(); + dependencies.add(project.getDependencies().create(gav)); + if (platformDependencyProvider.isPresent()) { + if (!ModuleDependency.class.isAssignableFrom( + platformDependencyProvider.get().getClass())) { + throw new IllegalArgumentException("Unable to determine dependency '" + + platformDependencyProvider.get().getName() + "' type"); + } + + ModuleDependency platformDependency = (ModuleDependency) platformDependencyProvider.get(); + // A platform dependency must have the platform attribute specified. + Category category = platformDependency.getAttributes().getAttribute(CATEGORY_ATTRIBUTE_TYPED); + if (category == null + || (!category.getName().equals(REGULAR_PLATFORM) + && !category.getName().equals(ENFORCED_PLATFORM))) { + throw new IllegalArgumentException( + "Dependency '" + platformDependency.getName() + "' is not a platform"); + } + dependencies.add(platformDependency); + } + + Configuration singleComponentVariantResolver = + project.getConfigurations().detachedConfiguration(dependencies.toArray(new Dependency[0])); singleComponentVariantResolver.setCanBeConsumed(false); singleComponentVariantResolver.shouldResolveConsistentlyWith(versionsSource); versionsSource.getAttributes().keySet().forEach(a -> { diff --git a/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy b/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy index 6f6f644..5b52c65 100644 --- a/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy +++ b/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy @@ -266,4 +266,25 @@ class EdgeCasesFunctionalTest extends Specification { expect: build() } + + def "resolve against a platform project if specified"() { + given: + buildFile << """ + val springBom = "org.springframework:spring-framework-bom:6.2.9" + dependencies { + implementation(platform(springBom)) + implementation("org.springframework:spring-jcl") + } + + extraJavaModuleInfo { + failOnAutomaticModules.set(true) + platformDependency.set(project.provider { project.dependencies.platform(project.dependencies.create(springBom)) as? org.gradle.api.artifacts.ModuleDependency }) + module("org.springframework:spring-jcl", "spring.jcl") + } + """ + + expect: + build() + } + } From 35760ec559f3731bd4cbc1e8e3cd7f67fa389f8c Mon Sep 17 00:00:00 2001 From: Tim Hurman Date: Wed, 5 Nov 2025 00:38:02 +0900 Subject: [PATCH 2/2] Syntax update and README update detailing feature --- README.md | 9 +++++++++ .../moduleinfo/test/EdgeCasesFunctionalTest.groovy | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 07fd12a..56af86c 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,15 @@ configurations.runtimeClasspath { } ``` +In addition to creating a common configuration for resolution, a platform project can also be enabled to enforce +versions. + +```kotlin +extraJavaModuleInfo { + platformDependency = project.provider { project.dependencies.platform(project.dependencies.create("org.example:bom:1.0.0")) } +} +``` + ## I have many automatic modules in my project. How can I convert them into proper modules and control what they export or require? The plugin provides a set of `moduleDescriptorRecommendations` tasks that generate the real module declarations utilizing [jdeps](https://docs.oracle.com/en/java/javase/11/tools/jdeps.html) and dependency metadata. diff --git a/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy b/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy index 5b52c65..bddc215 100644 --- a/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy +++ b/src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy @@ -278,7 +278,7 @@ class EdgeCasesFunctionalTest extends Specification { extraJavaModuleInfo { failOnAutomaticModules.set(true) - platformDependency.set(project.provider { project.dependencies.platform(project.dependencies.create(springBom)) as? org.gradle.api.artifacts.ModuleDependency }) + platformDependency.set(project.provider { project.dependencies.platform(project.dependencies.create(springBom)) }) module("org.springframework:spring-jcl", "spring.jcl") } """