Skip to content

Refactor AppCDS handling now that we support JEP 483 in addition to AppCDS #46234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
/**
* Indicates that a specific container image should be used to generate the AppCDS file
*/
public final class AppCDSContainerImageBuildItem extends SimpleBuildItem {
public final class JvmStartupOptimizerArchiveContainerImageBuildItem extends SimpleBuildItem {

private final String containerImage;

public AppCDSContainerImageBuildItem(String containerImage) {
public JvmStartupOptimizerArchiveContainerImageBuildItem(String containerImage) {
this.containerImage = containerImage;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.deployment.pkg.builditem;

import java.nio.file.Path;

import io.quarkus.builder.item.SimpleBuildItem;

/**
* Build item to indicate to the various steps that generation
* of a JVM startup archive has been requested
*/
public final class JvmStartupOptimizerArchiveRequestedBuildItem extends SimpleBuildItem {

/**
* Directory where various files needed for JVM startup archive generation will reside
*/
private final Path dir;
private final JvmStartupOptimizerArchiveType type;

public JvmStartupOptimizerArchiveRequestedBuildItem(Path dir, JvmStartupOptimizerArchiveType type) {
this.dir = dir;
this.type = type;
}

public Path getDir() {
return dir;
}

public JvmStartupOptimizerArchiveType getType() {
return type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.quarkus.deployment.pkg.builditem;

import java.nio.file.Path;

import io.quarkus.builder.item.SimpleBuildItem;

/**
* A build item containing the result of the JVM startup archive generation process
*/
public final class JvmStartupOptimizerArchiveResultBuildItem extends SimpleBuildItem {

/**
* The file containing the generated archive
*/
private final Path archive;
/**
* The type of archive generated
*/
private final JvmStartupOptimizerArchiveType type;

public JvmStartupOptimizerArchiveResultBuildItem(Path archive) {
this(archive, JvmStartupOptimizerArchiveType.AppCDS);
}

public JvmStartupOptimizerArchiveResultBuildItem(Path archive, JvmStartupOptimizerArchiveType type) {
this.archive = archive;
this.type = type;
}

public Path getArchive() {
return archive;
}

public JvmStartupOptimizerArchiveType getType() {
return type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@
import io.quarkus.deployment.builditem.TransformedClassesBuildItem;
import io.quarkus.deployment.configuration.ClassLoadingConfig;
import io.quarkus.deployment.pkg.PackageConfig;
import io.quarkus.deployment.pkg.builditem.AppCDSRequestedBuildItem;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.deployment.pkg.builditem.JarBuildItem;
import io.quarkus.deployment.pkg.builditem.JvmStartupOptimizerArchiveRequestedBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageSourceJarBuildItem;
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.deployment.pkg.builditem.UberJarIgnoredResourceBuildItem;
Expand Down Expand Up @@ -198,10 +198,12 @@ public JarBuildItem buildRunnerJar(CurateOutcomeBuildItem curateOutcomeBuildItem
List<UberJarIgnoredResourceBuildItem> uberJarIgnoredResourceBuildItems,
QuarkusBuildCloseablesBuildItem closeablesBuildItem,
List<AdditionalApplicationArchiveBuildItem> additionalApplicationArchiveBuildItems,
MainClassBuildItem mainClassBuildItem, Optional<AppCDSRequestedBuildItem> appCDS) throws Exception {
MainClassBuildItem mainClassBuildItem,
Optional<JvmStartupOptimizerArchiveRequestedBuildItem> jvmStartupOptimizerArchiveRequested)
throws Exception {

if (appCDS.isPresent()) {
handleAppCDSSupportFileGeneration(transformedClasses, generatedClasses, appCDS.get());
if (jvmStartupOptimizerArchiveRequested.isPresent()) {
handleAppCDSSupportFileGeneration(transformedClasses, generatedClasses, jvmStartupOptimizerArchiveRequested.get());
}

return switch (packageConfig.jar().type()) {
Expand All @@ -224,9 +226,11 @@ public JarBuildItem buildRunnerJar(CurateOutcomeBuildItem curateOutcomeBuildItem
// the idea here is to just dump the class names of the generated and transformed classes into a file
// that is read at runtime when AppCDS generation is requested
private void handleAppCDSSupportFileGeneration(TransformedClassesBuildItem transformedClasses,
List<GeneratedClassBuildItem> generatedClasses, AppCDSRequestedBuildItem appCDS) throws IOException {
Path appCDsDir = appCDS.getAppCDSDir();
Path generatedClassesFile = appCDsDir.resolve("generatedAndTransformed.lst");
List<GeneratedClassBuildItem> generatedClasses,
JvmStartupOptimizerArchiveRequestedBuildItem jvmStartupOptimizerArchiveRequested)
throws IOException {
Path dir = jvmStartupOptimizerArchiveRequested.getDir();
Path generatedClassesFile = dir.resolve("generatedAndTransformed.lst");
try (BufferedWriter writer = Files.newBufferedWriter(generatedClassesFile, StandardOpenOption.CREATE)) {
StringBuilder classes = new StringBuilder();
for (GeneratedClassBuildItem generatedClass : generatedClasses) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.pkg.PackageConfig;
import io.quarkus.deployment.pkg.builditem.AppCDSContainerImageBuildItem;
import io.quarkus.deployment.pkg.builditem.AppCDSRequestedBuildItem;
import io.quarkus.deployment.pkg.builditem.AppCDSResultBuildItem;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.CompiledJavaVersionBuildItem;
import io.quarkus.deployment.pkg.builditem.JarBuildItem;
import io.quarkus.deployment.pkg.builditem.JvmStartupOptimizerArchiveContainerImageBuildItem;
import io.quarkus.deployment.pkg.builditem.JvmStartupOptimizerArchiveRequestedBuildItem;
import io.quarkus.deployment.pkg.builditem.JvmStartupOptimizerArchiveResultBuildItem;
import io.quarkus.deployment.pkg.builditem.JvmStartupOptimizerArchiveType;
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.deployment.steps.MainClassBuildStep;
Expand All @@ -44,30 +44,31 @@ public class JvmStartupOptimizerArchiveBuildStep {

@BuildStep(onlyIf = AppCDSRequired.class)
public void requested(PackageConfig packageConfig, OutputTargetBuildItem outputTarget,
BuildProducer<AppCDSRequestedBuildItem> producer)
BuildProducer<JvmStartupOptimizerArchiveRequestedBuildItem> producer)
throws IOException {
Path appCDSDir = outputTarget.getOutputDirectory().resolve("appcds");
IoUtils.createOrEmptyDir(appCDSDir);
Path archiveDir = outputTarget.getOutputDirectory().resolve("jvmstartuparchive");
IoUtils.createOrEmptyDir(archiveDir);

producer.produce(new AppCDSRequestedBuildItem(outputTarget.getOutputDirectory().resolve("appcds"),
packageConfig.jar().appcds().useAot() ? JvmStartupOptimizerArchiveType.AOT
: JvmStartupOptimizerArchiveType.AppCDS));
producer.produce(
new JvmStartupOptimizerArchiveRequestedBuildItem(outputTarget.getOutputDirectory().resolve("jvmstartuparchive"),
packageConfig.jar().appcds().useAot() ? JvmStartupOptimizerArchiveType.AOT
: JvmStartupOptimizerArchiveType.AppCDS));
}

@BuildStep(onlyIfNot = NativeOrNativeSourcesBuild.class)
public void build(Optional<AppCDSRequestedBuildItem> requested,
public void build(Optional<JvmStartupOptimizerArchiveRequestedBuildItem> requested,
JarBuildItem jarResult, OutputTargetBuildItem outputTarget, PackageConfig packageConfig,
CompiledJavaVersionBuildItem compiledJavaVersion,
Optional<AppCDSContainerImageBuildItem> appCDSContainerImage,
BuildProducer<AppCDSResultBuildItem> appCDS,
Optional<JvmStartupOptimizerArchiveContainerImageBuildItem> jvmStartupOptimizerArchiveContainerImage,
BuildProducer<JvmStartupOptimizerArchiveResultBuildItem> jvmStartupOptimizerArchive,
BuildProducer<ArtifactResultBuildItem> artifactResult) throws Exception {
if (requested.isEmpty()) {
return;
}

// to actually execute the commands needed to generate the AppCDS file, either the JVM in the container image will be used
// (if specified), or the JVM running the build
String containerImage = determineContainerImage(packageConfig, appCDSContainerImage);
String containerImage = determineContainerImage(packageConfig, jvmStartupOptimizerArchiveContainerImage);
String javaBinPath = null;
if (containerImage == null) {
javaBinPath = System.getProperty("java.home") + File.separator + "bin" + File.separator
Expand Down Expand Up @@ -117,18 +118,18 @@ public void build(Optional<AppCDSRequestedBuildItem> requested,
}
}

appCDS.produce(new AppCDSResultBuildItem(archivePath));
jvmStartupOptimizerArchive.produce(new JvmStartupOptimizerArchiveResultBuildItem(archivePath));
artifactResult.produce(new ArtifactResultBuildItem(archivePath, "appCDS", Collections.emptyMap()));
}

private String determineContainerImage(PackageConfig packageConfig,
Optional<AppCDSContainerImageBuildItem> appCDSContainerImage) {
Optional<JvmStartupOptimizerArchiveContainerImageBuildItem> jvmStartupOptimizerArchiveContainer) {
if (!packageConfig.jar().appcds().useContainer()) {
return null;
} else if (packageConfig.jar().appcds().builderImage().isPresent()) {
return packageConfig.jar().appcds().builderImage().get();
} else if (appCDSContainerImage.isPresent()) {
return appCDSContainerImage.get().getContainerImage();
} else if (jvmStartupOptimizerArchiveContainer.isPresent()) {
return jvmStartupOptimizerArchiveContainer.get().getContainerImage();
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@
import io.quarkus.deployment.configuration.RunTimeConfigurationGenerator;
import io.quarkus.deployment.naming.NamingConfig;
import io.quarkus.deployment.pkg.PackageConfig;
import io.quarkus.deployment.pkg.builditem.AppCDSControlPointBuildItem;
import io.quarkus.deployment.pkg.builditem.AppCDSRequestedBuildItem;
import io.quarkus.deployment.recording.BytecodeRecorderImpl;
import io.quarkus.dev.appstate.ApplicationStateNotification;
import io.quarkus.dev.console.QuarkusConsole;
Expand All @@ -77,7 +75,6 @@
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.gizmo.TryBlock;
import io.quarkus.runtime.Application;
import io.quarkus.runtime.ApplicationLifecycleManager;
import io.quarkus.runtime.ExecutionModeManager;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.NativeImageRuntimePropertiesRecorder;
Expand All @@ -87,7 +84,6 @@
import io.quarkus.runtime.StartupContext;
import io.quarkus.runtime.StartupTask;
import io.quarkus.runtime.annotations.QuarkusMain;
import io.quarkus.runtime.appcds.AppCDSUtil;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.quarkus.runtime.util.StepTiming;

Expand Down Expand Up @@ -140,8 +136,6 @@ void build(List<StaticBytecodeRecorderBuildItem> staticInitTasks,
LiveReloadBuildItem liveReloadBuildItem,
ApplicationInfoBuildItem applicationInfo,
List<AllowJNDIBuildItem> allowJNDIBuildItems,
Optional<AppCDSRequestedBuildItem> appCDSRequested,
Optional<AppCDSControlPointBuildItem> appCDSControlPoint,
NamingConfig namingConfig) {

appClassNameProducer.produce(new ApplicationClassNameBuildItem(Application.APP_CLASS_NAME));
Expand Down Expand Up @@ -224,22 +218,6 @@ void build(List<StaticBytecodeRecorderBuildItem> staticInitTasks,
mv = file.getMethodCreator("doStart", void.class, String[].class);
mv.setModifiers(Modifier.PROTECTED | Modifier.FINAL);

// if AppCDS generation was requested and no other code has requested handling of the process,
// we ensure that the application simply loads some classes from a file and terminates
if (appCDSRequested.isPresent() && appCDSControlPoint.isEmpty()) {
ResultHandle createAppCDsSysProp = mv.invokeStaticMethod(
ofMethod(System.class, "getProperty", String.class, String.class, String.class),
mv.load(GENERATE_APP_CDS_SYSTEM_PROPERTY), mv.load("false"));
ResultHandle createAppCDSBool = mv.invokeStaticMethod(
ofMethod(Boolean.class, "parseBoolean", boolean.class, String.class), createAppCDsSysProp);
BytecodeCreator createAppCDS = mv.ifTrue(createAppCDSBool).trueBranch();

createAppCDS.invokeStaticMethod(ofMethod(AppCDSUtil.class, "loadGeneratedClasses", void.class));

createAppCDS.invokeStaticMethod(ofMethod(ApplicationLifecycleManager.class, "exit", void.class));
createAppCDS.returnValue(null);
}

// Make sure we set properties in doStartup as well. This is necessary because setting them in the static-init
// sets them at build-time, on the host JVM, while SVM has substitutions for System. get/ setProperty at
// run-time which will never see those properties unless we also set them at run-time.
Expand Down

This file was deleted.

Loading
Loading