Update AOT work to latest Framework code
This commit is contained in:
@@ -42,6 +42,8 @@ public class GenerateAotSources extends JavaExec {
|
||||
|
||||
private final DirectoryProperty resourcesDir;
|
||||
|
||||
private final DirectoryProperty classesDir;
|
||||
|
||||
private final Property<String> groupId;
|
||||
|
||||
private final Property<String> artifactId;
|
||||
@@ -50,6 +52,7 @@ public class GenerateAotSources extends JavaExec {
|
||||
this.applicationClass = getProject().getObjects().property(String.class);
|
||||
this.sourcesDir = getProject().getObjects().directoryProperty();
|
||||
this.resourcesDir = getProject().getObjects().directoryProperty();
|
||||
this.classesDir = getProject().getObjects().directoryProperty();
|
||||
this.groupId = getProject().getObjects().property(String.class);
|
||||
this.artifactId = getProject().getObjects().property(String.class);
|
||||
getMainClass().set("org.springframework.boot.AotProcessor");
|
||||
@@ -80,6 +83,11 @@ public class GenerateAotSources extends JavaExec {
|
||||
return this.resourcesDir;
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public DirectoryProperty getClassesDir() {
|
||||
return this.classesDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TaskAction
|
||||
public void exec() {
|
||||
@@ -87,6 +95,7 @@ public class GenerateAotSources extends JavaExec {
|
||||
args.add(this.applicationClass.get());
|
||||
args.add(this.sourcesDir.getAsFile().get().getAbsolutePath());
|
||||
args.add(this.resourcesDir.getAsFile().get().getAbsolutePath());
|
||||
args.add(this.classesDir.getAsFile().get().getAbsolutePath());
|
||||
args.addAll(super.getArgs());
|
||||
this.setArgs(args);
|
||||
super.exec();
|
||||
|
||||
@@ -39,7 +39,7 @@ public class AotGenerateTests {
|
||||
mavenBuild.project("aot").goals("package").execute((project) -> {
|
||||
Path aotDirectory = project.toPath().resolve("target/spring-aot/main");
|
||||
assertThat(collectRelativeFileNames(aotDirectory.resolve("sources")))
|
||||
.containsOnly("org/test/SampleApplication__ApplicationContextInitializer.java");
|
||||
.contains("org/test/SampleApplication__ApplicationContextInitializer.java");
|
||||
assertThat(collectRelativeFileNames(aotDirectory.resolve("resources"))).containsOnly(
|
||||
"META-INF/native-image/org.springframework.boot.maven.it/aot/reflect-config.json",
|
||||
"META-INF/native-image/org.springframework.boot.maven.it/aot/native-image.properties");
|
||||
|
||||
@@ -73,6 +73,12 @@ public class AotGenerateMojo extends AbstractRunMojo {
|
||||
@Parameter(defaultValue = "${project.build.directory}/spring-aot/main/resources", required = true)
|
||||
private File generatedResources;
|
||||
|
||||
/**
|
||||
* Directory containing the generated classes.
|
||||
*/
|
||||
@Parameter(defaultValue = "${project.build.directory}/spring-aot/main/classes", required = true)
|
||||
private File generatedClasses;
|
||||
|
||||
@Override
|
||||
protected void run(File workingDirectory, String startClassName, Map<String, String> environmentVariables)
|
||||
throws MojoExecutionException, MojoFailureException {
|
||||
@@ -90,6 +96,7 @@ public class AotGenerateMojo extends AbstractRunMojo {
|
||||
private void deletePreviousAotAssets() {
|
||||
FileSystemUtils.deleteRecursively(this.generatedSources);
|
||||
FileSystemUtils.deleteRecursively(this.generatedResources);
|
||||
FileSystemUtils.deleteRecursively(this.generatedClasses);
|
||||
}
|
||||
|
||||
private void generateAotAssets(File workingDirectory, String startClassName,
|
||||
@@ -102,6 +109,7 @@ public class AotGenerateMojo extends AbstractRunMojo {
|
||||
args.add(startClassName);
|
||||
args.add(this.generatedSources.toString());
|
||||
args.add(this.generatedResources.toString());
|
||||
args.add(this.generatedClasses.toString());
|
||||
args.add(this.project.getGroupId());
|
||||
args.add(this.project.getArtifactId());
|
||||
addArgs(args);
|
||||
|
||||
@@ -24,18 +24,19 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
|
||||
import org.springframework.aot.generator.GeneratedType;
|
||||
import org.springframework.aot.generator.GeneratedTypeReference;
|
||||
import org.springframework.aot.generate.DefaultGenerationContext;
|
||||
import org.springframework.aot.generate.FileSystemGeneratedFiles;
|
||||
import org.springframework.aot.generate.GeneratedFiles.Kind;
|
||||
import org.springframework.aot.hint.ExecutableHint;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.nativex.FileNativeConfigurationWriter;
|
||||
import org.springframework.context.generator.ApplicationContextAotGenerator;
|
||||
import org.springframework.context.aot.ApplicationContextAotGenerator;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -45,10 +46,14 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
public class AotProcessor {
|
||||
|
||||
private static final Consumer<ExecutableHint.Builder> INVOKE_CONSTRUCTOR_HINT = (hint) -> hint
|
||||
.setModes(ExecutableMode.INVOKE);
|
||||
|
||||
private final Class<?> application;
|
||||
|
||||
private final String[] applicationArgs;
|
||||
@@ -57,6 +62,8 @@ public class AotProcessor {
|
||||
|
||||
private final Path resourceOutput;
|
||||
|
||||
private final Path classOutput;
|
||||
|
||||
private final String groupId;
|
||||
|
||||
private final String artifactId;
|
||||
@@ -67,17 +74,19 @@ public class AotProcessor {
|
||||
* @param applicationArgs the arguments to provide to the main method
|
||||
* @param sourceOutput the location of generated sources
|
||||
* @param resourceOutput the location of generated resources
|
||||
* @param classOutput the location of generated classes
|
||||
* @param groupId the group ID of the application, used to locate
|
||||
* native-image.properties
|
||||
* @param artifactId the artifact ID of the application, used to locate
|
||||
* native-image.properties
|
||||
*/
|
||||
public AotProcessor(Class<?> application, String[] applicationArgs, Path sourceOutput, Path resourceOutput,
|
||||
String groupId, String artifactId) {
|
||||
Path classOutput, String groupId, String artifactId) {
|
||||
this.application = application;
|
||||
this.applicationArgs = applicationArgs;
|
||||
this.sourceOutput = sourceOutput;
|
||||
this.resourceOutput = resourceOutput;
|
||||
this.classOutput = classOutput;
|
||||
this.groupId = groupId;
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
@@ -104,36 +113,39 @@ public class AotProcessor {
|
||||
}
|
||||
|
||||
private void performAotProcessing(GenericApplicationContext applicationContext) {
|
||||
DefaultGeneratedTypeContext generationContext = new DefaultGeneratedTypeContext(
|
||||
this.application.getPackageName(), (packageName) -> GeneratedType.of(ClassName.get(packageName,
|
||||
this.application.getSimpleName() + "__ApplicationContextInitializer")));
|
||||
FileSystemGeneratedFiles generatedFiles = new FileSystemGeneratedFiles(this::getRoot);
|
||||
DefaultGenerationContext generationContext = new DefaultGenerationContext(generatedFiles);
|
||||
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
|
||||
generator.generateApplicationContext(applicationContext, generationContext);
|
||||
|
||||
// Register reflection hint for entry point as we access it via reflection
|
||||
generationContext.runtimeHints().reflection()
|
||||
.registerType(GeneratedTypeReference.of(generationContext.getMainGeneratedType().getClassName()),
|
||||
(hint) -> hint.onReachableType(TypeReference.of(this.application)).withConstructor(
|
||||
Collections.emptyList(),
|
||||
(constructorHint) -> constructorHint.setModes(ExecutableMode.INVOKE)));
|
||||
|
||||
writeGeneratedSources(generationContext.toJavaFiles());
|
||||
writeGeneratedResources(generationContext.runtimeHints());
|
||||
ClassName generatedInitializerClassName = generationContext.getClassNameGenerator()
|
||||
.generateClassName(this.application, "ApplicationContextInitializer");
|
||||
generator.generateApplicationContext(applicationContext, generationContext, generatedInitializerClassName);
|
||||
registerEntryPointHint(generationContext, generatedInitializerClassName);
|
||||
generationContext.writeGeneratedContent();
|
||||
writeHints(generationContext.getRuntimeHints());
|
||||
writeNativeImageProperties();
|
||||
}
|
||||
|
||||
private void writeGeneratedSources(List<JavaFile> sources) {
|
||||
for (JavaFile source : sources) {
|
||||
try {
|
||||
source.writeTo(this.sourceOutput);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to write " + source.typeSpec.name, ex);
|
||||
}
|
||||
}
|
||||
private void registerEntryPointHint(DefaultGenerationContext generationContext,
|
||||
ClassName generatedInitializerClassName) {
|
||||
TypeReference generatedType = TypeReference.of(generatedInitializerClassName.canonicalName());
|
||||
TypeReference applicationType = TypeReference.of(this.application);
|
||||
generationContext.getRuntimeHints().reflection().registerType(generatedType, (hint) -> hint
|
||||
.onReachableType(applicationType).withConstructor(Collections.emptyList(), INVOKE_CONSTRUCTOR_HINT));
|
||||
}
|
||||
|
||||
private void writeGeneratedResources(RuntimeHints hints) {
|
||||
private Path getRoot(Kind kind) {
|
||||
switch (kind) {
|
||||
case SOURCE:
|
||||
return this.sourceOutput;
|
||||
case RESOURCE:
|
||||
return this.resourceOutput;
|
||||
case CLASS:
|
||||
return this.classOutput;
|
||||
}
|
||||
throw new IllegalStateException("Unsupported kind " + kind);
|
||||
}
|
||||
|
||||
private void writeHints(RuntimeHints hints) {
|
||||
FileNativeConfigurationWriter writer = new FileNativeConfigurationWriter(this.resourceOutput, this.groupId,
|
||||
this.artifactId);
|
||||
writer.write(hints);
|
||||
@@ -164,21 +176,22 @@ public class AotProcessor {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int requiredArgs = 5;
|
||||
int requiredArgs = 6;
|
||||
if (args.length < requiredArgs) {
|
||||
throw new IllegalArgumentException("Usage: " + AotProcessor.class.getName()
|
||||
+ " <applicationName> <sourceOutput> <resourceOutput> <groupId> <artifactId> <originalArgs...>");
|
||||
+ " <applicationName> <sourceOutput> <resourceOutput> <classOutput> <groupId> <artifactId> <originalArgs...>");
|
||||
}
|
||||
String applicationName = args[0];
|
||||
Path sourceOutput = Paths.get(args[1]);
|
||||
Path resourceOutput = Paths.get(args[2]);
|
||||
String groupId = args[3];
|
||||
String artifactId = args[4];
|
||||
Path classOutput = Paths.get(args[3]);
|
||||
String groupId = args[4];
|
||||
String artifactId = args[5];
|
||||
String[] applicationArgs = (args.length > requiredArgs) ? Arrays.copyOfRange(args, requiredArgs, args.length)
|
||||
: new String[0];
|
||||
Class<?> application = Class.forName(applicationName);
|
||||
AotProcessor aotProcess = new AotProcessor(application, applicationArgs, sourceOutput, resourceOutput, groupId,
|
||||
artifactId);
|
||||
AotProcessor aotProcess = new AotProcessor(application, applicationArgs, sourceOutput, resourceOutput,
|
||||
classOutput, groupId, artifactId);
|
||||
aotProcess.process();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class AotProcessorTests {
|
||||
void processApplicationInvokesRunMethod(@TempDir Path directory) {
|
||||
String[] arguments = new String[] { "1", "2" };
|
||||
AotProcessor processor = new AotProcessor(SampleApplication.class, arguments, directory.resolve("source"),
|
||||
directory.resolve("resource"), "com.example", "example");
|
||||
directory.resolve("resource"), directory.resolve("class"), "com.example", "example");
|
||||
processor.process();
|
||||
assertThat(SampleApplication.argsHolder).isEqualTo(arguments);
|
||||
assertThat(directory).satisfies(hasGeneratedAssetsForSampleApplication());
|
||||
@@ -54,7 +54,7 @@ class AotProcessorTests {
|
||||
@Test
|
||||
void processApplicationWithMainMethodThatDoesNotRun(@TempDir Path directory) {
|
||||
AotProcessor processor = new AotProcessor(BrokenApplication.class, new String[0], directory.resolve("source"),
|
||||
directory.resolve("resource"), "com.example", "example");
|
||||
directory.resolve("resource"), directory.resolve("class"), "com.example", "example");
|
||||
assertThatIllegalArgumentException().isThrownBy(processor::process)
|
||||
.withMessageContaining("Does it run a SpringApplication?");
|
||||
assertThat(directory).isEmptyDirectory();
|
||||
@@ -63,8 +63,8 @@ class AotProcessorTests {
|
||||
@Test
|
||||
void invokeMainParseArgumentsAndInvokesRunMethod(@TempDir Path directory) throws Exception {
|
||||
String[] mainArguments = new String[] { SampleApplication.class.getName(),
|
||||
directory.resolve("source").toString(), directory.resolve("resource").toString(), "com.example",
|
||||
"example", "1", "2" };
|
||||
directory.resolve("source").toString(), directory.resolve("resource").toString(),
|
||||
directory.resolve("class").toString(), "com.example", "example", "1", "2" };
|
||||
AotProcessor.main(mainArguments);
|
||||
assertThat(SampleApplication.argsHolder).containsExactly("1", "2");
|
||||
assertThat(directory).satisfies(hasGeneratedAssetsForSampleApplication());
|
||||
@@ -78,8 +78,8 @@ class AotProcessorTests {
|
||||
|
||||
private Consumer<Path> hasGeneratedAssetsForSampleApplication() {
|
||||
return (directory) -> {
|
||||
assertThat(directory
|
||||
.resolve("source/org/springframework/boot/SampleApplication__ApplicationContextInitializer.java"))
|
||||
assertThat(directory.resolve(
|
||||
"source/org/springframework/boot/AotProcessorTests_SampleApplication__ApplicationContextInitializer.java"))
|
||||
.exists().isRegularFile();
|
||||
assertThat(directory.resolve("resource/META-INF/native-image/com.example/example/reflect-config.json"))
|
||||
.exists().isRegularFile();
|
||||
|
||||
Reference in New Issue
Block a user