Skip to content

Commit f61c546

Browse files
committed
Work around Kotlin plugin breaking AOT compile classpaths
Compilation of AOT-generated source code requires runtime dependencies to be on the classpath. This is necessary as a class from a runtime dependency may appear in the signature of a generated method that defines a bean. To accomplish this, Boot's AOT plugin sets the org.gradle.usage attribute of the compile classpath configurations of the aot and aotTest source sets to java-runtime. When the Kotlin plugin is applied after Boot's AOT plugin it breaks this arrangement by setting org.gradle.usage to java-api. There doesn't appear to be a way to prevent it from messing with the aot and aotTest source sets despite them not using Kotlin. This commit works around the problem by repairing the damage and setting the attribute back to java-runtime again. Fixes gh-46397
1 parent 4a4ab26 commit f61c546

File tree

5 files changed

+136
-1
lines changed

5 files changed

+136
-1
lines changed

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/KotlinPluginAction.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@ class KotlinPluginAction implements PluginApplicationAction {
3434

3535
@Override
3636
public void execute(Project project) {
37+
configureKotlinVersionProperty(project);
38+
enableJavaParametersOption(project);
39+
repairDamageToAotCompileConfigurations(project);
40+
}
41+
42+
private void configureKotlinVersionProperty(Project project) {
3743
ExtraPropertiesExtension extraProperties = project.getExtensions().getExtraProperties();
3844
if (!extraProperties.has("kotlin.version")) {
3945
String kotlinVersion = getKotlinVersion(project);
4046
extraProperties.set("kotlin.version", kotlinVersion);
4147
}
42-
enableJavaParametersOption(project);
4348
}
4449

4550
private String getKotlinVersion(Project project) {
@@ -52,6 +57,13 @@ private void enableJavaParametersOption(Project project) {
5257
.configureEach((compile) -> compile.getKotlinOptions().setJavaParameters(true));
5358
}
5459

60+
private void repairDamageToAotCompileConfigurations(Project project) {
61+
SpringBootAotPlugin aotPlugin = project.getPlugins().findPlugin(SpringBootAotPlugin.class);
62+
if (aotPlugin != null) {
63+
aotPlugin.repairKotlinPluginDamage(project);
64+
}
65+
}
66+
5567
@Override
5668
public Class<? extends Plugin<? extends Project>> getPluginClass() {
5769
return KotlinPluginWrapper.class;

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,19 @@ private void addJUnitPlatformLauncherDependency(Project project, Configuration c
237237
dependencies.add(dependencyHandler.create("org.junit.platform:junit-platform-launcher"));
238238
}
239239

240+
void repairKotlinPluginDamage(Project project) {
241+
project.getPlugins().withType(JavaPlugin.class).configureEach((javaPlugin) -> {
242+
repairKotlinPluginDamage(project, SpringBootAotPlugin.AOT_SOURCE_SET_NAME);
243+
repairKotlinPluginDamage(project, SpringBootAotPlugin.AOT_TEST_SOURCE_SET_NAME);
244+
});
245+
}
246+
247+
private void repairKotlinPluginDamage(Project project, String sourceSetName) {
248+
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
249+
SourceSetContainer sourceSets = javaPluginExtension.getSourceSets();
250+
Configuration compileClasspath = project.getConfigurations()
251+
.getByName(sourceSets.getByName(sourceSetName).getCompileClasspathConfigurationName());
252+
configureJavaRuntimeUsageAttribute(project, compileClasspath.getAttributes());
253+
}
254+
240255
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.gradle.plugin;
1818

1919
import java.io.BufferedReader;
20+
import java.io.File;
2021
import java.io.IOException;
2122
import java.io.StringReader;
2223
import java.util.HashSet;
@@ -90,9 +91,31 @@ void taskConfigurationIsAvoided() throws IOException {
9091
assertThat(configured).containsExactlyInAnyOrder("help", "compileJava", "clean");
9192
}
9293

94+
@Test
95+
void compileAotJavaHasTransitiveRuntimeDependenciesOnItsClasspathWhenUsingKotlin() {
96+
expectConfigurationCacheRequestedDeprecationWarning();
97+
expectResolvableUsageIsAlreadyAllowedWarning();
98+
String output = this.gradleBuild.build("compileAotJavaClasspath").getOutput();
99+
assertThat(output).contains("org.jboss.logging" + File.separatorChar + "jboss-logging");
100+
}
101+
102+
@Test
103+
void compileAotTestJavaHasTransitiveRuntimeDependenciesOnItsClasspathWhenUsingKotlin() {
104+
expectConfigurationCacheRequestedDeprecationWarning();
105+
expectResolvableUsageIsAlreadyAllowedWarning();
106+
String output = this.gradleBuild.build("compileAotTestJavaClasspath").getOutput();
107+
assertThat(output).contains("org.jboss.logging" + File.separatorChar + "jboss-logging");
108+
}
109+
93110
private void expectConfigurationCacheRequestedDeprecationWarning() {
94111
this.gradleBuild.expectDeprecationWarningsWithAtLeastVersion("8.14")
95112
.expectDeprecationMessages("The StartParameter.isConfigurationCacheRequested property has been deprecated");
96113
}
97114

115+
private void expectResolvableUsageIsAlreadyAllowedWarning() {
116+
this.gradleBuild.expectDeprecationWarningsWithAtLeastVersion("8.4")
117+
.expectDeprecationMessages("The resolvable usage is already allowed on configuration "
118+
+ "':aotRuntimeClasspath'. This behavior has been deprecated.");
119+
}
120+
98121
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the License);
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
plugins {
18+
id 'org.springframework.boot'
19+
id 'org.springframework.boot.aot'
20+
id 'java'
21+
id 'org.jetbrains.kotlin.jvm'
22+
}
23+
24+
repositories {
25+
mavenCentral()
26+
}
27+
28+
dependencies {
29+
implementation "org.hibernate.orm:hibernate-core:6.1.1.Final"
30+
}
31+
32+
task('compileAotJavaClasspath') {
33+
doFirst {
34+
tasks.findByName('compileAotJava').classpath.files.each { println it }
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the License);
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
plugins {
18+
id 'org.springframework.boot'
19+
id 'org.springframework.boot.aot'
20+
id 'java'
21+
id "org.jetbrains.kotlin.jvm"
22+
}
23+
24+
repositories {
25+
mavenCentral()
26+
maven {
27+
url = 'repository'
28+
}
29+
}
30+
31+
configurations.all {
32+
resolutionStrategy {
33+
eachDependency {
34+
if (it.requested.group == 'org.springframework.boot') {
35+
it.useVersion project.bootVersion
36+
}
37+
}
38+
}
39+
}
40+
41+
dependencies {
42+
implementation "org.hibernate.orm:hibernate-core:6.1.1.Final"
43+
}
44+
45+
task('compileAotTestJavaClasspath') {
46+
doFirst {
47+
tasks.findByName('compileAotTestJava').classpath.files.each { println it }
48+
}
49+
}

0 commit comments

Comments
 (0)