Update AOT work to latest Framework code

pull/30880/head
Phillip Webb 3 years ago
parent 88250f92bd
commit 8cf63a28b8

@ -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 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 writeGeneratedResources(RuntimeHints hints) {
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();

Loading…
Cancel
Save