From 8fdb01b6983141d4874fd8e9bf8eb292c896b558 Mon Sep 17 00:00:00 2001 From: GT Date: Sat, 15 Aug 2026 23:01:16 +0200 Subject: [PATCH] Honor runtime classpath exclusions during AOT processing Signed-off-by: GT --- .../gradle/plugin/SpringBootAotPlugin.java | 18 +++++++ .../SpringBootAotPluginIntegrationTests.java | 12 +++++ ...AotHonorsRuntimeClasspathExclusions.gradle | 39 ++++++++++++++ ...onorsTestRuntimeClasspathExclusions.gradle | 52 +++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processAotHonorsRuntimeClasspathExclusions.gradle create mode 100644 build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHonorsTestRuntimeClasspathExclusions.gradle diff --git a/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java b/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java index f847c222d469..176e24b43b2d 100644 --- a/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java +++ b/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java @@ -16,6 +16,8 @@ package org.springframework.boot.gradle.plugin; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.stream.Stream; @@ -176,6 +178,7 @@ private Configuration createAotProcessingClasspath(Project project, String taskN classpath.setDescription("Classpath of the " + taskName + " task."); removeDevelopmentOnly(base.getExtendsFrom(), developmentOnlyConfigurationNames) .forEach(classpath::extendsFrom); + classpath.getIncoming().beforeResolve((dependencies) -> copyExcludeRules(base, classpath)); classpath.attributes((attributes) -> { ProviderFactory providers = project.getProviders(); AttributeContainer baseAttributes = base.getAttributes(); @@ -187,6 +190,21 @@ private Configuration createAotProcessingClasspath(Project project, String taskN }); } + private void copyExcludeRules(Configuration source, Configuration target) { + source.getExcludeRules().forEach((excludeRule) -> { + Map exclusion = new HashMap<>(); + String group = excludeRule.getGroup(); + String module = excludeRule.getModule(); + if (group != null) { + exclusion.put("group", group); + } + if (module != null) { + exclusion.put("module", module); + } + target.exclude(exclusion); + }); + } + private Stream removeDevelopmentOnly(Set configurations, Set developmentOnlyConfigurationNames) { return configurations.stream() diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java b/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java index 0291854f4c27..96181b93bb7e 100644 --- a/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java +++ b/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java @@ -94,12 +94,24 @@ void processAotHasTransitiveRuntimeDependenciesOnItsClasspath() { assertThat(output).contains("org.jboss.logging" + File.separatorChar + "jboss-logging"); } + @TestTemplate + void processAotHonorsRuntimeClasspathExclusions() { + String output = this.gradleBuild.build("processAotClasspath").getOutput(); + assertThat(output).doesNotContain("org.jboss.logging" + File.separatorChar + "jboss-logging"); + } + @TestTemplate void processTestAotHasTransitiveRuntimeDependenciesOnItsClasspath() { String output = this.gradleBuild.build("processTestAotClasspath").getOutput(); assertThat(output).contains("org.jboss.logging" + File.separatorChar + "jboss-logging"); } + @TestTemplate + void processTestAotHonorsTestRuntimeClasspathExclusions() { + String output = this.gradleBuild.build("processTestAotClasspath").getOutput(); + assertThat(output).doesNotContain("org.jboss.logging" + File.separatorChar + "jboss-logging"); + } + @TestTemplate void processAotDoesNotHaveDevelopmentOnlyDependenciesOnItsClasspath() { String output = this.gradleBuild.build("processAotClasspath").getOutput(); diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processAotHonorsRuntimeClasspathExclusions.gradle b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processAotHonorsRuntimeClasspathExclusions.gradle new file mode 100644 index 000000000000..666291b713e4 --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processAotHonorsRuntimeClasspathExclusions.gradle @@ -0,0 +1,39 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed 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. + */ + +plugins { + id 'org.springframework.boot' + id 'org.springframework.boot.aot' + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation "org.hibernate.orm:hibernate-core:6.1.1.Final" +} + +configurations.runtimeClasspath { + exclude group: "org.jboss.logging", module: "jboss-logging" +} + +task('processAotClasspath') { + doFirst { + tasks.findByName('processAot').classpath.files.each { println it } + } +} diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHonorsTestRuntimeClasspathExclusions.gradle b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHonorsTestRuntimeClasspathExclusions.gradle new file mode 100644 index 000000000000..485dd4d51327 --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHonorsTestRuntimeClasspathExclusions.gradle @@ -0,0 +1,52 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed 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. + */ + +plugins { + id 'org.springframework.boot' + id 'org.springframework.boot.aot' + id 'java' +} + +repositories { + mavenCentral() + maven { + url = 'repository' + } +} + +configurations.all { + resolutionStrategy { + eachDependency { + if (it.requested.group == 'org.springframework.boot') { + it.useVersion project.bootVersion + } + } + } +} + +dependencies { + testImplementation "org.hibernate.orm:hibernate-core:6.1.1.Final" +} + +configurations.testRuntimeClasspath { + exclude group: "org.jboss.logging", module: "jboss-logging" +} + +task('processTestAotClasspath') { + doFirst { + tasks.findByName('processTestAot').classpath.files.each { println it } + } +}