Skip to content
This repository was archived by the owner on Feb 23, 2023. It is now read-only.

Commit e278b9e

Browse files
committed
Disable devtools in AOT mode
Prior to this commit, Spring Native would fail if Spring Boot Devtools is detected on the classpath, mentioning an incompatibility with this library. Because Devtools is about recompiling user classes+restarting the application context during development, this approach is currently not compatible with AOT since AOT sources are not re-generated during this lifecycle. This commit now ignores completely Devtools during AOT generation and runtime in AOT mode. Closes gh-1419
1 parent 319e101 commit e278b9e

File tree

8 files changed

+44
-12
lines changed

8 files changed

+44
-12
lines changed

spring-aot-gradle-plugin/src/main/java/org/springframework/aot/gradle/SpringAotGradlePlugin.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.gradle.api.Plugin;
3131
import org.gradle.api.Project;
3232
import org.gradle.api.artifacts.Configuration;
33+
import org.gradle.api.artifacts.ConfigurationContainer;
3334
import org.gradle.api.artifacts.Dependency;
3435
import org.gradle.api.file.DuplicatesStrategy;
3536
import org.gradle.api.file.FileCollection;
@@ -112,7 +113,7 @@ public void apply(final Project project) {
112113
SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
113114

114115
// Create a detached configuration that holds dependencies for AOT generation
115-
SourceSet aotMainSourceSet = createAotMainSourceSet(sourceSets, generatedFilesPath);
116+
SourceSet aotMainSourceSet = createAotMainSourceSet(sourceSets, project.getConfigurations(), generatedFilesPath);
116117
GenerateAotSources generateAotSources = createGenerateAotSourcesTask(project, sourceSets);
117118
project.getTasks().named(aotMainSourceSet.getCompileJavaTaskName(), JavaCompile.class, (aotCompileJava) -> {
118119
aotCompileJava.source(generateAotSources.getSourcesOutputDirectory());
@@ -138,7 +139,7 @@ public void apply(final Project project) {
138139
});
139140

140141
// Create a detached configuration that holds dependencies for AOT test generation
141-
SourceSet aotTestSourceSet = createAotTestSourceSet(sourceSets, generatedFilesPath);
142+
SourceSet aotTestSourceSet = createAotTestSourceSet(sourceSets, project.getConfigurations(), generatedFilesPath);
142143
GenerateAotTestSources generateAotTestSources = createGenerateAotTestSourcesTask(project, sourceSets);
143144
project.getTasks().named(aotTestSourceSet.getCompileJavaTaskName(), JavaCompile.class, (aotTestCompileJava) -> {
144145
aotTestCompileJava.source(generateAotTestSources.getGeneratedSourcesOutputDirectory());
@@ -171,8 +172,8 @@ public void apply(final Project project) {
171172
nativeCompile.dependsOn(generatedSourcesJar);
172173
});
173174
graal.getBinaries().named(NativeImagePlugin.NATIVE_MAIN_EXTENSION).configure(options -> {
174-
Provider<RegularFile> generatedSources = generatedSourcesJar.getArchiveFile();
175-
options.classpath(generatedSources);
175+
Provider<RegularFile> generatedSources = generatedSourcesJar.getArchiveFile();
176+
options.classpath(generatedSources);
176177
});
177178
graal.getBinaries().named(NativeImagePlugin.NATIVE_TEST_EXTENSION).configure(options -> {
178179
Provider<RegularFile> generatedTestSources = generatedTestSourcesJar.getArchiveFile();
@@ -218,11 +219,13 @@ private Path createGeneratedSourcesFolder(Project project) {
218219
}
219220
}
220221

221-
private SourceSet createAotMainSourceSet(SourceSetContainer sourceSets, Path generatedFilesPath) {
222+
private SourceSet createAotMainSourceSet(SourceSetContainer sourceSets, ConfigurationContainer configurations, Path generatedFilesPath) {
222223
File aotSourcesDirectory = generatedFilesPath.resolve("sources").resolve(AOT_MAIN_SOURCE_SET_NAME).toFile();
223224
File aotResourcesDirectory = generatedFilesPath.resolve("resources").resolve(AOT_MAIN_SOURCE_SET_NAME).toFile();
224225
SourceSet aotMainSourceSet = sourceSets.create(AOT_MAIN_SOURCE_SET_NAME);
225-
aotMainSourceSet.setCompileClasspath(sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath());
226+
FileCollection aotCompileClasspath = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()
227+
.minus(configurations.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME));
228+
aotMainSourceSet.setCompileClasspath(aotCompileClasspath);
226229
aotMainSourceSet.getJava().setSrcDirs(Collections.singletonList(aotSourcesDirectory));
227230
aotMainSourceSet.getResources().setSrcDirs(Collections.singletonList(aotResourcesDirectory));
228231
return aotMainSourceSet;
@@ -268,11 +271,13 @@ private Configuration createAotGenerationConfiguration(Project project) {
268271
return detachedConfiguration;
269272
}
270273

271-
private SourceSet createAotTestSourceSet(SourceSetContainer sourceSets, Path generatedFilesPath) {
274+
private SourceSet createAotTestSourceSet(SourceSetContainer sourceSets, ConfigurationContainer configurations, Path generatedFilesPath) {
272275
File aotTestSourcesDirectory = generatedFilesPath.resolve("sources").resolve(AOT_TEST_SOURCE_SET_NAME).toFile();
273276
File aotTestResourcesDirectory = generatedFilesPath.resolve("resources").resolve(AOT_TEST_SOURCE_SET_NAME).toFile();
274277
SourceSet aotTestSourceSet = sourceSets.create(AOT_TEST_SOURCE_SET_NAME);
275-
aotTestSourceSet.setCompileClasspath(sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath());
278+
FileCollection aotTestCompileClasspath = sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath()
279+
.minus(configurations.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME));
280+
aotTestSourceSet.setCompileClasspath(aotTestCompileClasspath);
276281
aotTestSourceSet.getJava().setSrcDirs(Collections.singletonList(aotTestSourcesDirectory));
277282
aotTestSourceSet.getResources().setSrcDirs(Collections.singletonList(aotTestResourcesDirectory));
278283
return aotTestSourceSet;

spring-aot-gradle-plugin/src/test/java/org/springframework/aot/gradle/SpringAotGradlePluginTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,19 @@ void pluginRegistersAotSourceSet() {
7373
assertThat(aotSourceSet.getCompileClasspath()).containsAll(mainSourceSet.getRuntimeClasspath());
7474
}
7575

76+
@Test
77+
void devtoolsIsIgnoredDuringAotGeneration() {
78+
Project project = createTestProject();
79+
project.getDependencies().add(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME, "org.springframework.boot:spring-boot-devtools");
80+
JavaPluginExtension java = project.getExtensions().findByType(JavaPluginExtension.class);
81+
SourceSet aotSourceSet = java.getSourceSets().findByName(SpringAotGradlePlugin.AOT_MAIN_SOURCE_SET_NAME);
82+
assertThat(aotSourceSet).isNotNull();
83+
assertThat(aotSourceSet.getCompileClasspath().getFiles()).noneMatch(file -> file.getName().contains("spring-boot-devtools"));
84+
SourceSet aotTestSourceSet = java.getSourceSets().findByName(SpringAotGradlePlugin.AOT_TEST_SOURCE_SET_NAME);
85+
assertThat(aotTestSourceSet).isNotNull();
86+
assertThat(aotTestSourceSet.getCompileClasspath().getFiles()).noneMatch(file -> file.getName().contains("spring-boot-devtools"));
87+
}
88+
7689
@Test
7790
void pluginRegistersAotTestSourceSet() {
7891
Project project = createTestProject();

spring-aot-maven-plugin/src/main/java/org/springframework/aot/maven/GenerateMojo.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.HashSet;
2626
import java.util.List;
2727
import java.util.Set;
28+
import java.util.stream.Collectors;
2829

2930
import org.apache.maven.model.Resource;
3031
import org.apache.maven.plugin.MojoExecutionException;
@@ -88,7 +89,10 @@ public void execute() throws MojoExecutionException, MojoFailureException {
8889
Path sourcesPath = this.generatedSourcesDirectory.toPath().resolve(Paths.get("src", "main", "java"));
8990
Path resourcesPath = this.generatedSourcesDirectory.toPath().resolve(Paths.get("src", "main", "resources"));
9091
try {
91-
List<String> runtimeClasspathElements = project.getRuntimeClasspathElements();
92+
List<String> runtimeClasspathElements = project.getRuntimeClasspathElements()
93+
.stream()
94+
.filter(element -> !element.contains("spring-boot-devtools"))
95+
.collect(Collectors.toList());
9296

9397
findJarFile(this.pluginArtifacts, "org.springframework.experimental", "spring-native-configuration")
9498
.ifPresent(artifact -> prependDependency(artifact, runtimeClasspathElements));

spring-aot-maven-plugin/src/main/java/org/springframework/aot/maven/TestGenerateMojo.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.HashSet;
2828
import java.util.List;
2929
import java.util.Set;
30+
import java.util.stream.Collectors;
3031

3132
import org.apache.maven.model.Resource;
3233
import org.apache.maven.plugin.MojoExecutionException;
@@ -79,7 +80,10 @@ public void execute() throws MojoExecutionException, MojoFailureException {
7980
return;
8081
}
8182
try {
82-
List<String> testClasspathElements = this.project.getTestClasspathElements();
83+
List<String> testClasspathElements = this.project.getTestClasspathElements()
84+
.stream()
85+
.filter(element -> !element.contains("spring-boot-devtools"))
86+
.collect(Collectors.toList());
8387
Files.createDirectories(this.generatedTestSourcesDirectory.toPath());
8488

8589
Path sourcesPath = this.generatedTestSourcesDirectory.toPath().resolve(Paths.get("src", "test", "java"));

spring-aot/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@
191191
<artifactId>spring-boot-starter-security</artifactId>
192192
<scope>test</scope>
193193
</dependency>
194+
<dependency>
195+
<groupId>org.springframework.boot</groupId>
196+
<artifactId>spring-boot-devtools</artifactId>
197+
<scope>test</scope>
198+
</dependency>
194199
<dependency>
195200
<groupId>org.jetbrains.kotlin</groupId>
196201
<artifactId>kotlin-stdlib-jdk8</artifactId>

spring-aot/src/main/java/org/springframework/aot/factories/DefaultFactoriesCodeContributor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private boolean passesFilterCheck(BuildContext context, SpringFactory factory) {
8787
} else if (factoryName.equals("org.springframework.boot.env.YamlPropertySourceLoader")) {
8888
return !aotOptions.isRemoveYamlSupport();
8989
} else if (factoryName.startsWith("org.springframework.boot.devtools")) {
90-
throw new IllegalStateException("Devtools is not supported yet, please remove the related dependency for now.");
90+
return false;
9191
}
9292
return true;
9393
}

spring-native-docs/src/main/asciidoc/build-setup.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ This means that running the application or its tests from the IDE or the command
242242
Any application using Spring AOT can use the `springAot` System property in order to use the AOT classes with a regular JVM.
243243
This is mainly useful for debugging purposes in case of issues during native image generation.
244244

245+
NOTE: When AOT more is enabled, Spring Boot Developer Tools are ignored as they are not compatible with an AOT approach.
246+
245247
You can set such a property when running an executable Jar from the command line:
246248

247249
[source,bash,subs="attributes,verbatim"]

spring-native/src/main/java/org/springframework/nativex/utils/NativeUtils.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public static Properties getNativeProperties() {
2626
props.put("spring.cloud.refresh.enabled", "false"); // Sampler is a class and can't be proxied
2727
props.put("spring.sleuth.async.enabled", "false"); // Too much proxy created
2828
props.put("spring.cloud.compatibility-verifier.enabled", "false"); // To avoid false positive due to SpringApplication patched copy
29-
props.put("spring.devtools.restart.enabled", "false"); // Deactivate dev tools
3029
return props;
3130
}
3231
}

0 commit comments

Comments
 (0)