diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java index 5f1c2d4d6bfa..21d90dd3e9e9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java @@ -44,6 +44,8 @@ enum ArtifactType { EXECUTABLE("executables"), /* Native image layer. */ IMAGE_LAYER("image_layer"), + /* Native image layer bundle. */ + IMAGE_LAYER_BUNDLE("image_layer_bundle"), /* For all shared libraries that are not JDK-related and needed at run-time. */ SHARED_LIBRARY("shared_libraries"), diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 5d84fb812d66..bf5c380a3d0b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -34,7 +34,6 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.UUID; import java.util.function.Predicate; @@ -44,6 +43,7 @@ import org.graalvm.nativeimage.ImageInfo; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platform.HOSTED_ONLY; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.c.libc.LibCBase; @@ -64,6 +64,7 @@ import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.thread.VMOperationControl; import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; @@ -493,9 +494,19 @@ public static void setImageLayerCreateEnabledHandler(OptionEnabledHandler LinkerRPath = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); - @OptionMigrationMessage("Use the '-o' option instead.")// - @Option(help = "Directory of the image file to be generated", type = OptionType.User)// - public static final HostedOptionKey Path = new HostedOptionKey<>(null); + @Platforms(HOSTED_ONLY.class) + public static Path getImagePath(OptionValues optionValues) { + VMError.guarantee(optionValues != null); + if (!ConcealedOptions.Path.hasBeenSet(optionValues)) { + VMError.shouldNotReachHere("Image builder requires %s", SubstrateOptionsParser.commandArgument(ConcealedOptions.Path, "")); + } + return Path.of(ConcealedOptions.Path.getValue(optionValues)); + } + + @Platforms(HOSTED_ONLY.class) + public static Path getImagePath() { + return getImagePath(HostedOptionValues.singleton()); + } public static final class GCGroup implements APIOptionGroup { @Override @@ -1025,9 +1036,10 @@ public static boolean useDebugInfoGeneration() { @Option(help = "Temporary option to disable checking of image builder module dependencies or increasing its verbosity", type = OptionType.Debug)// public static final HostedOptionKey CheckBootModuleDependencies = new HostedOptionKey<>(ModuleSupport.modulePathBuild ? 1 : 0); + @Platforms(HOSTED_ONLY.class) public static Path getDebugInfoSourceCacheRoot() { try { - return Paths.get(Path.getValue()).resolve(DebugInfoSourceCacheRoot.getValue()); + return SubstrateOptions.getImagePath().resolve(DebugInfoSourceCacheRoot.getValue()); } catch (InvalidPathException ipe) { throw UserError.invalidOptionValue(DebugInfoSourceCacheRoot, DebugInfoSourceCacheRoot.getValue(), "The path is invalid"); } @@ -1144,6 +1156,10 @@ protected void onValueUpdate(EconomicMap, Object> values, Integer o /** Use {@link SubstrateOptions#codeAlignment()} instead. */ @Option(help = "Alignment of AOT and JIT compiled code in bytes. The default of 0 automatically selects a suitable value.")// public static final HostedOptionKey CodeAlignment = new HostedOptionKey<>(0); + + @OptionMigrationMessage("Use the '-o' option instead.")// + @Option(help = "Directory of the image file to be generated", type = OptionType.User)// + public static final HostedOptionKey Path = new HostedOptionKey<>(null); } @Fold @@ -1205,12 +1221,13 @@ public Boolean getValue(OptionValues values) { @Option(help = "file:doc-files/FlightRecorderOptionsHelp.txt")// public static final RuntimeOptionKey FlightRecorderOptions = new RuntimeOptionKey<>("", Immutable); + @Platforms(HOSTED_ONLY.class) public static String reportsPath() { Path reportsPath = ImageSingletons.lookup(ReportingSupport.class).reportsPath; if (reportsPath.isAbsolute()) { return reportsPath.toString(); } - return Paths.get(Path.getValue()).resolve(reportsPath).toString(); + return getImagePath().resolve(reportsPath).toString(); } public static class ReportingSupport { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/NativeImageLayers.md b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/NativeImageLayers.md index 4e736db551eb..d9823844c8b5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/NativeImageLayers.md +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/NativeImageLayers.md @@ -63,11 +63,11 @@ base-layer.so # initial layer (includes VM/JDK code) To create and use layers `native-image` accepts two options: `--layer-create` and `--layer-use`. -First, `--layer-create` builds an image layer archive from code available on the class or module path: +### Option `--layer-create` builds an image layer archive from code available on the class or module path: ``` ---layer-create=[layer-file.nil][,module=][,package=] - builds an image layer file from the modules and packages specified by "module" and "package". +--layer-create=[layer-file.nil][,module=][,package=][,path=] + builds an image layer file from the modules and packages specified by "module" and "package" or "path". The file name, if specified, must be a simple file name, i.e., not contain any path separators, and have the *.nil extension. Otherwise, the layer-file name is derived from the image name. This will generate a Native Image Layer archive file containing metadata required to build @@ -78,7 +78,58 @@ First, `--layer-create` builds an image layer archive from code available on the A layer archive file has a _.nil_ extension, acronym for **N**ative **I**mage **L**ayer. -Second, `--layer-use` consumes a shared layer, and can extend it or create a final executable: +#### `--layer-create` suboptions + +The **module** and **package** and **path** suboptions of `--layer-create` are used to specify the classes and resources that should be included in the layer. +Within a `--layer-create` option argument, these suboption arguments can be specified multiple times, for example: +``` +--layer-create=base-layer.nil,package=ch.qos.logback.core.hook,package=ch.qos.logback.core.html,... +``` + +##### `--layer-create` suboption `module=` + +With this suboption, layer creation is instructed to make all packages and all resources that are part of the given module included in the layer. +In the future we will eventually provide a solution to refine the resource inclusion for the module suboption to allow fine-grained control over the included resources. + +##### `--layer-create` suboption `package=` + +Suboption `package` allows the inclusion of individual Java packages. For this kind of inclusion it does not matter if the specified package is from a classpath-entry or part of a module, both are supported. +Contrary to the `module` suboption, resources are not also automatically included. If resource inclusion is needed, the usual ways can be used (`resource-config.json`, `reachability-metadata.json` or resource related Feature API). + +##### `--layer-create` suboption `path=` + +This is a convenience suboption that requires a `classpath entry`. +If the provided entry is not also specified in the classpath of the given `native-image` invocation, an error message is shown. +All classes and all resources from the given classpath entry are included in the layer. Note that using this suboption might lead to larger than necessary layers. Only use this suboption for uses-cases where this is not an issue. + +#### `--layer-create` option argument file + +For complex use-cases, the `--layer-create` option argument can become very large. +To make this more manageable it is possible to have the `--layer-create` option argument specified via a separate file where each line corresponds to an entry of the option argument. + +This currently only works if the `--layer-create` option is specified from a `native-image.properties` file in a `META-INF/native-image` subdirectory. +If this requirement is met, the following can be used: +```properties +Args = --layer-create=@layer-create.args +``` +The `layer-create.args` file-path is relative to the directory that contains the `native-image.properties` file and might look like this: +``` +base-layer.nil +module=java.base +# micronaut and dependencies +package=io.micronaut.* +package=io.netty.* +package=jakarta.* +package=com.fasterxml.jackson.* +package=org.slf4j.* +# io.projectreactor:reactor-core and dependencies +package=reactor.* +package=org.reactivestreams.* +``` +Each line corresponds to one entry in the list of comma-separated entries that can usually be found in a regular `--layer-create` argument. +Lines starting with `#` are ignored and can therefore be used to provide comments in such an option argument file. + +### Option `--layer-use` consumes a shared layer, and can extend it or create a final executable: ``` --layer-use=layer-file.nil diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java index 951e57e42b3e..85eeeeb54657 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java @@ -267,7 +267,7 @@ private static String oR(OptionKey option) { final String oHModule = oH(SubstrateOptions.Module); final String oHClass = oH(SubstrateOptions.Class); final String oHName = oH(SubstrateOptions.Name); - final String oHPath = oH(SubstrateOptions.Path); + final String oHPath = oH(SubstrateOptions.ConcealedOptions.Path); final String oHUseLibC = oH(SubstrateOptions.UseLibC); final String oHEnableStaticExecutable = oHEnabled(SubstrateOptions.StaticExecutable); final String oHEnableSharedLibraryFlagPrefix = oHEnabled + SubstrateOptions.SharedLibrary.getName(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 43233a5a5ba5..71e8b882582e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -35,7 +35,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -1877,8 +1876,7 @@ public static Path getOutputDirectory() { } public static Path generatedFiles(OptionValues optionValues) { - String pathName = SubstrateOptions.Path.getValue(optionValues); - Path path = FileSystems.getDefault().getPath(pathName); + Path path = SubstrateOptions.getImagePath(optionValues); if (!Files.exists(path)) { try { Files.createDirectories(path); @@ -1887,7 +1885,7 @@ public static Path generatedFiles(OptionValues optionValues) { } } if (!Files.isDirectory(path)) { - throw VMError.shouldNotReachHere("Output path is not a directory: " + pathName); + throw VMError.shouldNotReachHere("Output path is not a directory: " + path); } return path.toAbsolutePath(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java index 3f775ec9680a..3ac7a4f509ea 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java @@ -71,6 +71,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.code.CEntryPointData; import com.oracle.svm.hosted.image.AbstractImage.NativeImageKind; +import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import com.oracle.svm.hosted.option.HostedOptionParser; import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.LogUtils; @@ -410,16 +411,16 @@ private int buildImage(ImageClassLoader classLoader) { NativeImageKind imageKind = null; boolean isStaticExecutable = SubstrateOptions.StaticExecutable.getValue(parsedHostedOptions); boolean isSharedLibrary = SubstrateOptions.SharedLibrary.getValue(parsedHostedOptions); - boolean isImageLayer = SubstrateOptions.LayerCreate.hasBeenSet(parsedHostedOptions); + boolean layerCreateOptionEnabled = HostedImageLayerBuildingSupport.isLayerCreateOptionEnabled(parsedHostedOptions); if (isStaticExecutable && isSharedLibrary) { reportConflictingOptions(SubstrateOptions.SharedLibrary, SubstrateOptions.StaticExecutable); - } else if (isStaticExecutable && isImageLayer) { + } else if (isStaticExecutable && layerCreateOptionEnabled) { reportConflictingOptions(SubstrateOptions.StaticExecutable, SubstrateOptions.LayerCreate); - } else if (isSharedLibrary && isImageLayer) { + } else if (isSharedLibrary && layerCreateOptionEnabled) { reportConflictingOptions(SubstrateOptions.SharedLibrary, SubstrateOptions.LayerCreate); } else if (isSharedLibrary) { imageKind = NativeImageKind.SHARED_LIBRARY; - } else if (isImageLayer) { + } else if (layerCreateOptionEnabled) { imageKind = NativeImageKind.IMAGE_LAYER; } else if (isStaticExecutable) { imageKind = NativeImageKind.STATIC_EXECUTABLE; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/diagnostic/HostedHeapDumpFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/diagnostic/HostedHeapDumpFeature.java index 671c4694e033..1c2e89902afd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/diagnostic/HostedHeapDumpFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/diagnostic/HostedHeapDumpFeature.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -35,19 +34,19 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.graal.compiler.options.Option; - import com.oracle.graal.pointsto.reports.ReportUtils; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; -import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; +import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.util.StringUtil; +import jdk.graal.compiler.options.Option; + @AutomaticallyRegisteredFeature public class HostedHeapDumpFeature implements InternalFeature { @@ -150,7 +149,7 @@ private void dumpHeap(String reason) { private static Path getDumpLocation() { try { - Path folder = Paths.get(Paths.get(SubstrateOptions.Path.getValue()).toString(), "dumps").toAbsolutePath(); + Path folder = SubstrateOptions.getImagePath().resolve("dumps").toAbsolutePath(); return Files.createDirectories(folder); } catch (IOException e) { throw new Error("Cannot create heap dumps directory.", e); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/driver/LayerOptionsSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/driver/LayerOptionsSupport.java index c29ab711161d..1ab8538c8f9b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/driver/LayerOptionsSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/driver/LayerOptionsSupport.java @@ -29,7 +29,6 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.imagelayer.LayerArchiveSupport; public class LayerOptionsSupport extends IncludeOptionsSupport { @@ -44,16 +43,8 @@ public static LayerOption parse(String layerOptionValue) { public static LayerOption parse(List options) { VMError.guarantee(!options.isEmpty()); - // Check for the optional file name - Path fileName = null; - int skip = 0; - if (options.getFirst().endsWith(LayerArchiveSupport.LAYER_FILE_EXTENSION)) { - fileName = Path.of(options.getFirst()); - skip = 1; - } - ExtendedOption[] extendedOptions = options.stream().skip(skip).map(ExtendedOption::parse).toArray(ExtendedOption[]::new); - return new LayerOption(fileName, extendedOptions); + ExtendedOption[] extendedOptions = options.stream().skip(1).map(ExtendedOption::parse).toArray(ExtendedOption[]::new); + return new LayerOption(Path.of(options.getFirst()), extendedOptions); } } - } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java index bf50d52a3034..176f8b4bcbf7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java @@ -42,13 +42,12 @@ import com.oracle.svm.core.BuildArtifacts; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; -import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; -import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.option.LocatableMultiOptionValue.ValueWithOrigin; import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.ArchiveSupport; +import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.NativeImageClassLoaderSupport; @@ -158,59 +157,63 @@ public Class lookupClass(boolean optional, String className) { */ public static void processLayerOptions(EconomicMap, Object> values, NativeImageClassLoaderSupport classLoaderSupport) { OptionValues hostedOptions = new OptionValues(values); - if (SubstrateOptions.LayerCreate.hasBeenSet(hostedOptions)) { - /* The last value wins, GR-55565 will warn about the overwritten values. */ - ValueWithOrigin valueWithOrigin = SubstrateOptions.LayerCreate.getValue(hostedOptions).lastValueWithOrigin().orElseThrow(); - String layerCreateValue = String.join(",", OptionUtils.resolveOptionValuesRedirection(SubstrateOptions.LayerCreate, valueWithOrigin)); - if (layerCreateValue.isEmpty()) { - /* Nothing to do, an empty --layer-create= disables the layer creation. */ - } else { - LayerOption layerOption = LayerOption.parse(layerCreateValue); - classLoaderSupport.setLayerFile(layerOption.fileName()); - String layerCreateArg = SubstrateOptionsParser.commandArgument(SubstrateOptions.LayerCreate, layerCreateValue); - NativeImageClassLoaderSupport.IncludeSelectors layerSelectors = classLoaderSupport.getLayerSelectors(); - for (IncludeOptionsSupport.ExtendedOption option : layerOption.extendedOptions()) { - IncludeOptionsSupport.parseIncludeSelector(layerCreateArg, valueWithOrigin, layerSelectors, option, layerCreatePossibleOptions()); - } + if (isLayerCreateOptionEnabled(hostedOptions)) { + ValueWithOrigin valueWithOrigin = getLayerCreateValueWithOrigin(hostedOptions); + String layerCreateValue = getLayerCreateValue(valueWithOrigin); + LayerOption layerOption = LayerOption.parse(layerCreateValue); + String layerCreateArg = SubstrateOptionsParser.commandArgument(SubstrateOptions.LayerCreate, layerCreateValue); + Path layerFileName = layerOption.fileName(); + if (layerFileName.toString().isEmpty()) { + layerFileName = Path.of(SubstrateOptions.Name.getValue(hostedOptions) + LayerArchiveSupport.LAYER_FILE_EXTENSION); + } + if (layerFileName.getParent() != null) { + throw UserError.abort("The given layer file '%s' in layer creation option '%s' from %s must be a simple file name, i.e., no path separators are allowed.", + layerFileName, layerCreateArg, valueWithOrigin.origin()); + } + Path layerFile = SubstrateOptions.getImagePath(hostedOptions).resolve(layerFileName); + classLoaderSupport.setLayerFile(layerFile); - SubstrateOptions.UseBaseLayerInclusionPolicy.update(values, true); - SubstrateOptions.ClosedTypeWorld.update(values, false); - if (SubstrateOptions.imageLayerEnabledHandler != null) { - SubstrateOptions.imageLayerEnabledHandler.onOptionEnabled(values); - } - if (SubstrateOptions.imageLayerCreateEnabledHandler != null) { - SubstrateOptions.imageLayerCreateEnabledHandler.onOptionEnabled(values); - } - SubstrateOptions.UseContainerSupport.update(values, false); - enableConservativeUnsafeAccess(values); + NativeImageClassLoaderSupport.IncludeSelectors layerSelectors = classLoaderSupport.getLayerSelectors(); + for (IncludeOptionsSupport.ExtendedOption option : layerOption.extendedOptions()) { + IncludeOptionsSupport.parseIncludeSelector(layerCreateArg, valueWithOrigin, layerSelectors, option, layerCreatePossibleOptions()); } + + SubstrateOptions.UseBaseLayerInclusionPolicy.update(values, true); + SubstrateOptions.ClosedTypeWorld.update(values, false); + if (SubstrateOptions.imageLayerEnabledHandler != null) { + SubstrateOptions.imageLayerEnabledHandler.onOptionEnabled(values); + } + if (SubstrateOptions.imageLayerCreateEnabledHandler != null) { + SubstrateOptions.imageLayerCreateEnabledHandler.onOptionEnabled(values); + } + SubstrateOptions.UseContainerSupport.update(values, false); + enableConservativeUnsafeAccess(values); } - if (SubstrateOptions.LayerUse.hasBeenSet(hostedOptions)) { - /* The last value wins, GR-55565 will warn about the overwritten values. */ - Path layerUseValue = SubstrateOptions.LayerUse.getValue(hostedOptions).lastValue().orElseThrow(); - if (layerUseValue.toString().isEmpty()) { - /* Nothing to do, an empty --layer-use= disables the layer application. */ - } else { - SubstrateOptions.ClosedTypeWorldHubLayout.update(values, false); - SubstrateOptions.ParseRuntimeOptions.update(values, false); - if (SubstrateOptions.imageLayerEnabledHandler != null) { - SubstrateOptions.imageLayerEnabledHandler.onOptionEnabled(values); - } + + if (isLayerUseOptionEnabled(hostedOptions)) { + SubstrateOptions.ClosedTypeWorldHubLayout.update(values, false); + SubstrateOptions.ParseRuntimeOptions.update(values, false); + if (SubstrateOptions.imageLayerEnabledHandler != null) { + SubstrateOptions.imageLayerEnabledHandler.onOptionEnabled(values); } enableConservativeUnsafeAccess(values); } } + private static Path getLayerUseValue(OptionValues hostedOptions) { + return SubstrateOptions.LayerUse.getValue(hostedOptions).lastValue().orElseThrow(); + } + /** * The default unsafe implementation assumes that all unsafe load/store operations can be * tracked. This cannot be used in layered images. - * + * * First, in the base layer we cannot track the future layers' unsafe accessed fields, so all * unsafe loads must be conservatively saturated. Similarly, we cannot see any unsafe writes * coming from future layers so all unsafe accessed fields are injected with all instantiated * subtypes of their declared type. - * + * * Second, application layer unsafe accessed fields cannot be linked to the base layer unsafe * writes, so again they are injected with all instantiated subtypes of their declared type. * Unsafe loads in the application layer are similarly saturated since we don't keep track of @@ -222,17 +225,31 @@ private static void enableConservativeUnsafeAccess(EconomicMap, Obj PointstoOptions.UseConservativeUnsafeAccess.update(values, true); } - private static boolean isLayerOptionEnabled(HostedOptionKey> option, HostedOptionValues values) { - if (option.hasBeenSet(values)) { - Object lastOptionValue = option.getValue(values).lastValue().orElseThrow(); - return !lastOptionValue.toString().isEmpty(); + private static ValueWithOrigin getLayerCreateValueWithOrigin(OptionValues hostedOptions) { + return SubstrateOptions.LayerCreate.getValue(hostedOptions).lastValueWithOrigin().orElseThrow(); + } + + public static boolean isLayerCreateOptionEnabled(OptionValues values) { + if (SubstrateOptions.LayerCreate.hasBeenSet(values)) { + return !getLayerCreateValue(getLayerCreateValueWithOrigin(values)).isEmpty(); + } + return false; + } + + private static String getLayerCreateValue(ValueWithOrigin valueWithOrigin) { + return String.join(",", OptionUtils.resolveOptionValuesRedirection(SubstrateOptions.LayerCreate, valueWithOrigin)); + } + + private static boolean isLayerUseOptionEnabled(OptionValues values) { + if (SubstrateOptions.LayerUse.hasBeenSet(values)) { + return !getLayerUseValue(values).toString().isEmpty(); } return false; } public static HostedImageLayerBuildingSupport initialize(HostedOptionValues values, ImageClassLoader imageClassLoader) { - boolean buildingSharedLayer = isLayerOptionEnabled(SubstrateOptions.LayerCreate, values); - boolean buildingExtensionLayer = isLayerOptionEnabled(SubstrateOptions.LayerUse, values); + boolean buildingSharedLayer = isLayerCreateOptionEnabled(values); + boolean buildingExtensionLayer = isLayerUseOptionEnabled(values); boolean buildingImageLayer = buildingSharedLayer || buildingExtensionLayer; boolean buildingInitialLayer = buildingImageLayer && !buildingExtensionLayer; @@ -252,7 +269,7 @@ public static HostedImageLayerBuildingSupport initialize(HostedOptionValues valu List snapshots = null; List graphsChannels = null; if (buildingExtensionLayer) { - Path layerFileName = SubstrateOptions.LayerUse.getValue(values).lastValue().orElseThrow(); + Path layerFileName = getLayerUseValue(values); loadLayerArchiveSupport = new LoadLayerArchiveSupport(layerFileName, archiveSupport); FilePaths filePaths = new FilePaths(loadLayerArchiveSupport.getSnapshotPath(), loadLayerArchiveSupport.getSnapshotGraphsPath()); List loadPaths = List.of(filePaths); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/WriteLayerArchiveSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/WriteLayerArchiveSupport.java index 938194568b4e..707b8a3333e5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/WriteLayerArchiveSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/WriteLayerArchiveSupport.java @@ -30,6 +30,7 @@ import java.util.jar.JarOutputStream; import com.oracle.svm.core.BuildArtifacts; +import com.oracle.svm.core.BuildArtifacts.ArtifactType; import com.oracle.svm.core.util.ArchiveSupport; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.NativeImageGenerator; @@ -50,9 +51,6 @@ private static Path validateLayerFile(Path layerFile) { if (fileName == null || !fileName.toString().endsWith(LAYER_FILE_EXTENSION)) { throw UserError.abort("The given layer file " + layerFile + " must end with '" + LAYER_FILE_EXTENSION + "'."); } - if (layerFile.getParent() != null) { - throw UserError.abort("The given layer file " + layerFile + " must be a simple file name, i.e., no path separators are allowed."); - } Path layerFilePath = layerFile.toAbsolutePath(); if (Files.isDirectory(layerFilePath)) { throw UserError.abort("The given layer file " + layerFile + " is a directory and not a file."); @@ -88,6 +86,7 @@ public void write(String imageName) { // copy the properties file to the jar Path propertiesFile = imageBuilderOutputDir.resolve(layerPropertiesFileName); archiveSupport.addFileToJar(imageBuilderOutputDir, propertiesFile, outputLayerLocation, jarOutStream); + BuildArtifacts.singleton().add(ArtifactType.IMAGE_LAYER_BUNDLE, outputLayerLocation); } catch (IOException e) { throw UserError.abort("Failed to create Native Image Layer file " + outputLayerLocation.getFileName(), e); } diff --git a/web-image/src/com.oracle.svm.hosted.webimage.test/src/com/oracle/svm/hosted/webimage/test/util/JTTTestSuite.java b/web-image/src/com.oracle.svm.hosted.webimage.test/src/com/oracle/svm/hosted/webimage/test/util/JTTTestSuite.java index 11feb3ce7bc7..33ce19029df0 100644 --- a/web-image/src/com.oracle.svm.hosted.webimage.test/src/com/oracle/svm/hosted/webimage/test/util/JTTTestSuite.java +++ b/web-image/src/com.oracle.svm.hosted.webimage.test/src/com/oracle/svm/hosted/webimage/test/util/JTTTestSuite.java @@ -271,7 +271,7 @@ protected static void compileToJS(WebImageOptions.VMType jsRuntime, Class cla List, Object>> hostedOptions = new ArrayList<>(); hostedOptions.add(Map.entry(SubstrateOptions.UnlockExperimentalVMOptions, true)); hostedOptions.add(Map.entry(SubstrateOptions.Name, VM_IMAGE_NAME)); - hostedOptions.add(Map.entry(SubstrateOptions.Path, getTestDir())); + hostedOptions.add(Map.entry(SubstrateOptions.ConcealedOptions.Path, getTestDir())); hostedOptions.add(Map.entry(WebImageOptions.SILENT_COMPILE, true)); hostedOptions.add(Map.entry(WebImageOptions.ClosureCompiler, WebImageTestOptions.USE_CLOSURE_COMPILER)); hostedOptions.add(Map.entry(WebImageOptions.DumpPreClosure, true));