Skip to content

Commit 2fafc12

Browse files
committed
Add Native Image Layers options.
1 parent 43c7d08 commit 2fafc12

File tree

17 files changed

+1110
-259
lines changed

17 files changed

+1110
-259
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ public class ImageLayerSnapshotUtil {
8888
public static final String IMAGE_SINGLETON_KEYS = "image singleton keys";
8989
public static final String IMAGE_SINGLETON_OBJECTS = "image singleton objects";
9090

91+
public static String snapshotFileName(String imageName) {
92+
return FILE_NAME_PREFIX + imageName + FILE_EXTENSION;
93+
}
94+
9195
public String getTypeIdentifier(AnalysisType type) {
9296
String javaName = type.toJavaName(true);
9397
return addModuleName(javaName, type.getJavaClass().getModule().getName());

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -107,21 +107,6 @@ public class SubstrateOptions {
107107
@Option(help = "Build shared library")//
108108
public static final HostedOptionKey<Boolean> SharedLibrary = new HostedOptionKey<>(false);
109109

110-
@Option(help = "Build a Native Image layer.")//
111-
public static final HostedOptionKey<Boolean> ImageLayer = new HostedOptionKey<>(false) {
112-
@Override
113-
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
114-
LayeredBaseImageAnalysis.update(values, newValue);
115-
ClosedTypeWorld.update(values, !newValue);
116-
StripDebugInfo.update(values, !newValue);
117-
AOTTrivialInline.update(values, !newValue);
118-
if (imageLayerEnabledHandler != null) {
119-
imageLayerEnabledHandler.onOptionEnabled(values);
120-
}
121-
UseContainerSupport.update(values, !newValue);
122-
}
123-
};
124-
125110
@APIOption(name = "static")//
126111
@Option(help = "Build statically linked executable (requires static libc and zlib)")//
127112
public static final HostedOptionKey<Boolean> StaticExecutable = new HostedOptionKey<>(false, key -> {
@@ -134,6 +119,14 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
134119
}
135120
});
136121

122+
// @APIOption(name = "layer-create")//
123+
@Option(help = "Experimental: Build a Native Image layer.")//
124+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> LayerCreate = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build());
125+
126+
// @APIOption(name = "layer-use")//
127+
@Option(help = "Experimental: Build an image based on a Native Image layer.")//
128+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> LayerUse = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build());
129+
137130
@APIOption(name = "libc")//
138131
@Option(help = "Selects the libc implementation to use. Available implementations: glibc, musl, bionic")//
139132
public static final HostedOptionKey<String> UseLibC = new HostedOptionKey<>(null) {
@@ -195,7 +188,7 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String ol
195188
public static final String IMAGE_MODULEPATH_PREFIX = "-imagemp";
196189
public static final String KEEP_ALIVE_PREFIX = "-keepalive";
197190
private static ValueUpdateHandler<OptimizationLevel> optimizeValueUpdateHandler;
198-
private static OptionEnabledHandler<Boolean> imageLayerEnabledHandler;
191+
public static OptionEnabledHandler<Boolean> imageLayerEnabledHandler;
199192

200193
@Fold
201194
public static boolean getSourceLevelDebug() {
@@ -1260,22 +1253,6 @@ public static boolean closedTypeWorld() {
12601253
@Option(help = "Enables logging of failed hash code injection", type = OptionType.Debug) //
12611254
public static final HostedOptionKey<Boolean> LoggingHashCodeInjection = new HostedOptionKey<>(false);
12621255

1263-
@Option(help = "Names of layer snapshots produced by PersistImageLayer", type = OptionType.Debug) //
1264-
@BundleMember(role = BundleMember.Role.Input)//
1265-
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Paths> LoadImageLayer = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Paths.build()) {
1266-
@Override
1267-
public void update(EconomicMap<OptionKey<?>, Object> values, Object boxedValue) {
1268-
super.update(values, boxedValue);
1269-
ClosedTypeWorld.update(values, false);
1270-
/* Ignore any potential undefined references caused by inlining in base layer. */
1271-
IgnoreUndefinedReferences.update(values, true);
1272-
AOTTrivialInline.update(values, false);
1273-
if (imageLayerEnabledHandler != null) {
1274-
imageLayerEnabledHandler.onOptionEnabled(values);
1275-
}
1276-
}
1277-
};
1278-
12791256
public static class TruffleStableOptions {
12801257

12811258
@Option(help = "Automatically copy the necessary language resources to the resources/languages directory next to the produced image." +
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
---
2+
layout: docs
3+
toc_group: build-overview
4+
link_title: Native Image Layers
5+
permalink: /reference-manual/native-image/overview/NativeImageLayers/
6+
---
7+
8+
# Native Image Layers
9+
10+
The Native Image Layers feature allows splitting an application and its required libraries into separate binaries: a
11+
thin layer executable supported by a chain of shared libraries.
12+
In contrast to regular Native Image building, this mode of operation enables sharing common libraries and VM/JDK code
13+
between multiple applications, resulting in reduced memory usage at run time.
14+
Moreover, it reduces the time to build an application since shared layers are only built once.
15+
16+
> Note this feature is experimental and under development.
17+
18+
### Table of Contents
19+
20+
* [Native Image Layers Architecture](#native-image-layers-architecture)
21+
* [Creating Native Image Layers](#creating-native-image-layers)
22+
* [Packaging Native Image Layers](#packaging-native-image-layers)
23+
24+
## Native Image Layers Architecture
25+
26+
When using Native Image Layers an application is logically composed of the final application executable plus one or more
27+
supporting layers containing code that the application requires.
28+
The supporting layers are called _shared layers_ and the application executable is referred to as either the
29+
_application layer_ or _executable layer_.
30+
The _initial_ or _base_ layer is a shared layer containing VM internals and core _java.base_ functionality at a minimum.
31+
It can also contain modules specific to a certain framework that the application may be built upon.
32+
We refer to any subsequent layer built on top of a shared layer as an _extension layer_.
33+
34+
At run time a shared layer is a shared object file on which other intermediate layers or executable application
35+
layers can be dependent.
36+
Thus, the application and its supporting shared layers form a chain:
37+
38+
```shell
39+
base-layer.so # initial layer (includes VM/JDK code)
40+
└── mid-layer.so # intermediate layer, depends on base-layer.so, adds extra functionality
41+
└── executable-image # final application executable, depends on mid-layer.so and base-layer.so
42+
```
43+
44+
This architecture enables the sharing of layers between applications when the hierarchy of layers forms a tree structure.
45+
For example, at run time there could be four applications that share one base layer and two intermediate layers:
46+
47+
```shell
48+
base-layer.so # initial layer (includes VM/JDK code)
49+
├── executable-image-0 # final application executable, depends on base-layer.so
50+
├── mid-layer-0.so # intermediate layer, depends on base-layer.so, adds extra functionality
51+
│ ├── executable-image-00 # final application executable, depends on mid-layer-0.so and base-layer.so
52+
│ └── executable-image-01 # final application executable, depends on mid-layer-0.so and base-layer.so
53+
└── mid-layer-1.so # intermediate layer, depends on base-layer.so, adds extra functionality
54+
└── executable-image-10 # final application executable, depends on mid-layer-1.so and base-layer.so
55+
```
56+
57+
> Note: The current implementation is limited to only a base layer and an application layer.
58+
59+
## Creating Native Image Layers
60+
61+
> Note: The API options described in this section are experimental and not yet released.
62+
> To interact with layers you must currently use their hosted variant: -H:LayerCreate and -H:LayerUse.
63+
64+
To create and use layers `native-image` accepts two options: `--layer-create` and `--layer-use`.
65+
66+
First, `--layer-create` builds an image layer archive from code available on the class or module path:
67+
68+
```
69+
--layer-create=[layer-file.nil][,module=<module-name>][,package=<package-name>]
70+
builds an image layer file from the modules and packages specified by "module" and "package".
71+
The file name, if specified, must be a simple file name, i.e., not contain any path separators,
72+
and have the *.nil extension. Otherwise, the layer-file name is derived from the image name.
73+
This will generate a Native Image Layers archive file containing metadata required to build
74+
either another layer or a final application executable that depends on this layer.
75+
The archive also contains the shared object file corresponding to this layer.
76+
If this options is specified with an empty value then it disables any prior layer creation option on the command line.
77+
```
78+
79+
A layer archive file has a _.nil_ extension, acronym for **N**ative **I**mage **L**ayer.
80+
81+
Second, `--layer-use` consumes a shared layer, and can extend it or create a final executable:
82+
83+
```
84+
--layer-use=layer-file.nil
85+
loads the given layer archive and makes it available to the build process.
86+
If option --layer-create=another-layer.nil is specified this creates a new layer that depends on the loaded layer.
87+
If no other layer option is specified this creates a final application executable that depends on the loaded layer.
88+
If this options is specified with an empty value then it disables any prior layer application option on the command line.
89+
```
90+
91+
Specifying each option more than once is allowed, however all but the last instance is ignored.
92+
Passing an empty value will disable the option, overwriting any previous value.
93+
These capabilities are useful in builds with long dependency chains where one may want to overwrite or disable layer
94+
related options from a library dependency.
95+
96+
#### Example Layers Option Usage
97+
98+
```shell
99+
# given an application that would usually be built like this
100+
native-image --module-path target/AwesomeLib-1.0-SNAPSHOT.jar --another-extra-option -cp . AwesomeHelloWorld
101+
102+
# you can now create a base-layer.nil layer from the java.base and java.awesome.lib modules
103+
native-image --layer-create=base-layer.nil,module=java.base,module=java.awesome.lib --module-path target/AwesomeLib-1.0-SNAPSHOT.jar
104+
105+
# then build the application on top of the preexisting base layer
106+
native-image --layer-use=base-layer.nil --module-path target/AwesomeLib-1.0-SNAPSHOT.jar --another-extra-option -cp . AwesomeHelloWorld
107+
108+
# extend the base layer with a mid layer that adds an extra module, chaining the layers together
109+
native-image --layer-use=base-layer.nil --layer-create=mid-layer.nil,module=java.ultimate.io.lib --module-path target/UltimateIoLib-1.0-SNAPSHOT.jar
110+
111+
# create an executable based on the mid layer (and implicitly on the base layer) and using the additional UltimateAwesomeHelloWorld.class from the classpath
112+
native-image --layer-use=mid-layer.nil --module-path target/AwesomeLib-1.0-SNAPSHOT.jar:target/UltimateIoLib-1.0-SNAPSHOT.jar -cp . UltimateAwesomeHelloWorld
113+
114+
# the same application can be built directly on the base-layer.nil
115+
native-image --layer-use=base-layer.nil --module-path target/AwesomeLib-1.0-SNAPSHOT.jar:target/UltimateIoLib-1.0-SNAPSHOT.jar -cp . UltimateAwesomeHelloWorld
116+
117+
# again, the same application can be built without any layers
118+
native-image --module-path target/AwesomeLib-1.0-SNAPSHOT.jar:target/UltimateIoLib-1.0-SNAPSHOT.jar -cp . UltimateAwesomeHelloWorld
119+
120+
# additionally a shared library can be built as a top layer containing the extra C entry points, based on a preexisting layer
121+
native-image --layer-use=base-layer.nil --module-path target/AwesomeLib-1.0-SNAPSHOT.jar --shared
122+
123+
# or without the layer
124+
native-image --module-path target/AwesomeLib-1.0-SNAPSHOT.jar --shared
125+
126+
```
127+
128+
### Invariants
129+
130+
1. Every image build only creates one layer.
131+
132+
2. The image build command used to create the application layer must work without `--layer-use` and create a standalone image.
133+
The standalone command must completely specify the module/classpath and all other necessary configuration options
134+
135+
3. The layer specified by `--layer-use` must be compatible with the standalone command line.
136+
The compatibility rules refer to:
137+
- class/jar file compatibility (`-cp`, `-p`, same JDK, same GraalVM, same libs, etc.)
138+
- config compatibility: GC config, etc.
139+
- Java system properties and Environment variables compatibility
140+
- access compatibility: no additional unsafe and field accesses are allowed
141+
142+
In case of incompatibility an error message is printed and the build process is aborted.
143+
144+
### Limitations
145+
146+
- Layers are platform dependent. A layer created with `--layer-create` on a specific OS/architecture
147+
cannot be loaded by `--layer-use` on a different OS/architecture.
148+
- Each layer can only depend on a previous layer. We explicitly make it impossible to depend on more than one layer to
149+
avoid any potential issues that can stem from _multiple inheritance_.
150+
- A shared layer is using the _.so_ extension to conform with the standard OS loader restrictions. However, it is not a
151+
standard shared library file, and it cannot be used with other applications.
152+
153+
## Packaging Native Image Layers
154+
155+
At build time a shared layer is stored in a layer archive that contains the following artifacts:
156+
157+
```shell
158+
[shared-layer.nil] # shared layer archive file
159+
├── shared-layer.json # snapshot of the shared layer metadata; used by subsequent build processes
160+
├── shared-layer.so # shared object of the shared layer; used by subsequent build processes and at run time
161+
└── shared-layer.properties # contains info about layer input data
162+
```
163+
164+
The layer snapshot file will be consumed by subsequent build processes that depend on this layer.
165+
It contains Native Image metadata, such as the analysis universe and available image singletons.
166+
The shared object file will be used at build time for symbol resolution, and at run time for application execution.
167+
The layer properties file contains metadata that uniquely identifies this layer: the options used to create the
168+
layer, all the input files and their checksum.
169+
Subsequent layer builds use the properties file to validate the layers they depend on: the JAR files that the build
170+
depends on must exactly match those that were used to build the previous layers.

0 commit comments

Comments
 (0)