Skip to content

Commit 137fd0b

Browse files
jchalouansalond
authored andcommitted
[GR-57838] Include language resources in native image.
PullRequest: graal/19430
2 parents 42a4082 + 2ab52a5 commit 137fd0b

File tree

12 files changed

+251
-72
lines changed

12 files changed

+251
-72
lines changed

docs/reference-manual/embedding/embed-languages.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -538,13 +538,17 @@ To build a native executable with the above configuration, run:
538538
mvn -Pnative package
539539
```
540540
541-
To build a native executable from a polyglot application, for example, a Java-host application embedding Python, a `./resources` directory containing all the required files is created by default.
542-
By default, the language runtime will look for the resources directory relative to the native executable or library image that was built.
541+
Building a native executable from a polyglot application, for example, a Java-host application embedding Python, automatically captures all the internal resources required by the included languages and tools.
542+
By default, the resources are included in the native executable itself.
543+
The inclusion of resources in the native executable can be disabled by `-H:-IncludeLanguageResources`.
544+
Another option is a separate _resources_ directory containing all the required files.
545+
To switch to this option, use `-H:+CopyLanguageResources`. This is the default behavior when `-H:+IncludeLanguageResources` is not supported, i.e., with Graal Languages earlier than 24.2.x (see the [versions roadmap](https://www.graalvm.org/release-calendar/)).
546+
When `-H:+CopyLanguageResources` is used, the language runtime will look for the resources directory relative to the native executable or the shared library.
543547
At run time, the lookup location may be customized using the `-Dpolyglot.engine.resourcePath=path/to/resources` option.
544-
To disable the resource creation, the `-H:-CopyLanguageResources` build-time option may be used.
545-
Note that some languages may not support running without a resources directory.
548+
To disable the capturing of resources altogether, add both `-H:-IncludeLanguageResources` and `-H:-CopyLanguageResources` to build-time options.
549+
Note that some languages may not support running without their resources.
546550
547-
With Polyglot version 23.1 the language home options like `-Dorg.graalvm.home` should no longer be used and were replaced with the resource directory option.
551+
With Graal Languages version 23.1 and newer the language home options like `-Dorg.graalvm.home` should no longer be used and were replaced with the resource directory option.
548552
The language home options remain functional for compatibility reasons but may be removed in future releases.
549553
550554
### Configuring Native Host Reflection

sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ For usages of the module-path pass the `--enable-native-access=org.graalvm.truff
1010
* GR-54300 `Context` and `Engine` is now automatically closed when no longer strongly referenced. A reachable `Value` or `PolyglotException` will keep the associated `Context` reachable. Additionally, the `Context` remains reachable when explicitly entered or if there is an active polyglot thread within it. The `Engine` remains reachable when there is a strongly reachable `Language`, `Instrument`, or `Context` instance. However, it is still recommended not to rely on garbage collection for closing. Instead, use the try-with-resources pattern for explicit context and engine management. For more information, refer to the [Automatic Close on GC documentation](https://github.com/oracle/graal/blob/master/truffle/docs/CloseOnGc.md).
1111
* GR-60022 Introduced the [FileSystem.newCompositeFileSystem](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#newCompositeFileSystem(org.graalvm.polyglot.io.FileSystem,org.graalvm.polyglot.io.FileSystem.Selector...)) method, which creates a composite `FileSystem` delegating operations to the specified delegate FileSystem instances based on the provided selectors.
1212
* GR-60022 Introduced the [FileSystem.newDenyIOFileSystem](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#newDenyIOFileSystem()) method, which creates a `FileSystem` that denies all file operations except for path parsing.
13+
* GR-57838 Added automatic inclusion of language and instrument resources for embedding Truffle languages in native image. We no longer produce a _resources_ folder next to the image by default. Documentation available [here](https://www.graalvm.org/reference-manual/embed-languages/#build-native-executables-from-polyglot-applications).
1314

1415
## Version 24.1.0
1516
* GR-51177 Enable random offsets of runtime compiled function entry points for the UNTRUSTED polyglot sandbox policy.

sdk/mx.sdk/mx_sdk_vm_impl.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,6 +2516,7 @@ def get_build_args(self):
25162516
'-H:APIFunctionPrefix=truffle_isolate_',
25172517
] + svm_experimental_options([
25182518
'-H:+IgnoreMaxHeapSizeWhileInVMOperation',
2519+
'-H:+CopyLanguageResources',
25192520
'-H:+GenerateBuildArtifactsFile', # generate 'build-artifacts.json'
25202521
]) + mx.get_runtime_jvm_args(self.subject.native_image_jar_distributions) + \
25212522
project.native_image_config.build_args + project.native_image_config.build_args_enterprise

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,10 +1331,11 @@ public static boolean useClosedTypeWorld() {
13311331

13321332
public static class TruffleStableOptions {
13331333

1334-
@Option(help = "Automatically copy the necessary language resources to the resources/languages directory next to the produced image." +
1335-
"Language resources for each language are specified in the native-image-resources.filelist file located in the language home directory." +
1336-
"If there is no native-image-resources.filelist file in the language home directory or the file is empty, then no resources are copied.", type = User, stability = OptionStability.STABLE)//
1337-
public static final HostedOptionKey<Boolean> CopyLanguageResources = new HostedOptionKey<>(true);
1334+
@Option(help = "Automatically copy the necessary language resources to the resources directory next to the produced image.", type = User, stability = OptionStability.STABLE)//
1335+
public static final HostedOptionKey<Boolean> CopyLanguageResources = new HostedOptionKey<>(false);
1336+
1337+
@Option(help = "Automatically include the necessary language internal resources in the produced image.", type = User, stability = OptionStability.STABLE)//
1338+
public static final HostedOptionKey<Boolean> IncludeLanguageResources = new HostedOptionKey<>(true);
13381339
}
13391340

13401341
@Option(help = "Reduce the amount of metadata in the image for implicit exceptions by removing inlining information from the stack trace. " +

substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java

Lines changed: 81 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ public boolean getAsBoolean() {
246246

247247
private boolean needsAllEncodings;
248248

249+
private boolean copyLanguageResources;
250+
private boolean includeLanguageResources;
251+
249252
private static void initializeTruffleReflectively(ClassLoader imageClassLoader) {
250253
invokeStaticMethod("com.oracle.truffle.api.impl.Accessor", "getTVMCI", Collections.emptyList());
251254
invokeStaticMethod("com.oracle.truffle.polyglot.InternalResourceCache", "initializeNativeImageState",
@@ -282,6 +285,16 @@ static <T> T invokeStaticMethod(String className, String methodName, Collection<
282285
}
283286
}
284287

288+
static boolean isStaticMethodPresent(String className, String methodName, Collection<Class<?>> parameterTypes) {
289+
try {
290+
Class<?> clazz = Class.forName(className);
291+
Method method = ReflectionUtil.lookupMethod(clazz, methodName, parameterTypes.toArray(new Class<?>[0]));
292+
return method != null;
293+
} catch (ReflectiveOperationException e) {
294+
throw VMError.shouldNotReachHere(e);
295+
}
296+
}
297+
285298
/**
286299
* Register all fields accessed by a InlinedField for an instance field or a static field as
287300
* unsafe accessed, which is necessary for correctness of the static analysis.
@@ -469,8 +482,31 @@ public void duringSetup(DuringSetupAccess a) {
469482

470483
StaticObjectSupport.duringSetup(a);
471484

485+
if (SubstrateOptions.TruffleStableOptions.CopyLanguageResources.hasBeenSet() && SubstrateOptions.TruffleStableOptions.IncludeLanguageResources.hasBeenSet() &&
486+
SubstrateOptions.TruffleStableOptions.CopyLanguageResources.getValue() && SubstrateOptions.TruffleStableOptions.IncludeLanguageResources.getValue()) {
487+
throw VMError.shouldNotReachHere("The options CopyLanguageResources and IncludeLanguageResources can't both be set to true!");
488+
}
489+
490+
copyLanguageResources = SubstrateOptions.TruffleStableOptions.CopyLanguageResources.getValue();
491+
includeLanguageResources = SubstrateOptions.TruffleStableOptions.IncludeLanguageResources.getValue();
492+
493+
if (includeLanguageResources && (!isStaticMethodPresent("com.oracle.truffle.polyglot.InternalResourceCache", "includeResourcesForNativeImage", List.of(Path.class, BiConsumer.class)) ||
494+
(HomeFinder.getInstance() != null && HomeFinder.getInstance().getLanguageHomes() != null && !HomeFinder.getInstance().getLanguageHomes().isEmpty()))) {
495+
// IncludeLanguageResources == true, but it is not supported.
496+
if (SubstrateOptions.TruffleStableOptions.IncludeLanguageResources.hasBeenSet()) {
497+
throw VMError.shouldNotReachHere("The option IncludeLanguageResources has been set to true, but it is not supported!");
498+
}
499+
if (!SubstrateOptions.TruffleStableOptions.CopyLanguageResources.hasBeenSet()) {
500+
copyLanguageResources = true;
501+
}
502+
}
503+
504+
if (copyLanguageResources) {
505+
includeLanguageResources = false;
506+
}
507+
472508
HomeFinder hf = HomeFinder.getInstance();
473-
if (SubstrateOptions.TruffleStableOptions.CopyLanguageResources.getValue()) {
509+
if (copyLanguageResources) {
474510
if (!(hf instanceof DefaultHomeFinder)) {
475511
VMError.shouldNotReachHere(String.format("HomeFinder %s cannot be used if CopyLanguageResources option of TruffleBaseFeature is enabled", hf.getClass().getName()));
476512
}
@@ -521,6 +557,38 @@ public void duringSetup(DuringSetupAccess a) {
521557
if (needsAllEncodings) {
522558
RuntimeResourceSupport.singleton().addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$", "Truffle needsAllEncodings flag is set");
523559
}
560+
561+
if (includeLanguageResources) {
562+
Path resourcesForNativeImageTempDir;
563+
try {
564+
resourcesForNativeImageTempDir = Files.createTempDirectory("resources_for_native_image");
565+
} catch (IOException e) {
566+
throw VMError.shouldNotReachHere("Unable to create temporary directory for truffle language resources", e);
567+
}
568+
try {
569+
invokeStaticMethod("com.oracle.truffle.polyglot.InternalResourceCache", "includeResourcesForNativeImage", List.of(Path.class, BiConsumer.class), resourcesForNativeImageTempDir,
570+
new BiConsumer<Module, Pair<String, byte[]>>() {
571+
@Override
572+
public void accept(Module module, Pair<String, byte[]> resource) {
573+
RuntimeResourceSupport.singleton().injectResource(module, resource.getLeft(), resource.getRight(), "Truffle Language Internal Resources");
574+
}
575+
});
576+
} catch (Exception e) {
577+
throw VMError.shouldNotReachHere("Unable to include truffle language resources in the image.", e);
578+
} finally {
579+
try (Stream<Path> filesToDelete = Files.walk(resourcesForNativeImageTempDir)) {
580+
filesToDelete.sorted(Comparator.reverseOrder()).forEach(f -> {
581+
try {
582+
Files.deleteIfExists(f);
583+
} catch (IOException ioe) {
584+
throw VMError.shouldNotReachHere("Unable to delete temporary directory for truffle language resources", ioe);
585+
}
586+
});
587+
} catch (IOException ioe) {
588+
throw VMError.shouldNotReachHere("Unable to delete temporary directory for truffle language resources", ioe);
589+
}
590+
}
591+
}
524592
}
525593

526594
static void checkTruffleFile(ClassInitializationSupport classInitializationSupport, TruffleFile file) {
@@ -570,7 +638,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
570638
registerInternalResourceFieldValueTransformers(config);
571639
}
572640

573-
private static void registerInternalResourceFieldValueTransformers(BeforeAnalysisAccessImpl config) {
641+
private void registerInternalResourceFieldValueTransformers(BeforeAnalysisAccessImpl config) {
574642
Class<?> internalResourceCacheClass = config.findClassByName("com.oracle.truffle.polyglot.InternalResourceCache");
575643
Class<?> internalResourceRootsClass = config.findClassByName("com.oracle.truffle.polyglot.InternalResourceRoots");
576644
Class<?> resetableCacheRootClass = config.findClassByName("com.oracle.truffle.polyglot.InternalResourceCache$ResettableCachedRoot");
@@ -587,6 +655,9 @@ private static void registerInternalResourceFieldValueTransformers(BeforeAnalysi
587655
config.registerFieldValueTransformer(ReflectionUtil.lookupField(false, internalResourceCacheClass, "path"), ResetFieldValueTransformer.INSTANCE);
588656
config.registerFieldValueTransformer(ReflectionUtil.lookupField(false, internalResourceRootsClass, "roots"), ResetFieldValueTransformer.INSTANCE);
589657
}
658+
if (!copyLanguageResources && !includeLanguageResources) {
659+
config.registerFieldValueTransformer(ReflectionUtil.lookupField(false, internalResourceCacheClass, "useInternalResources"), (receiver, originalValue) -> false);
660+
}
590661
}
591662

592663
private static final class ResetFieldValueTransformer implements FieldValueTransformer {
@@ -1133,7 +1204,7 @@ static void onBuildInvocation(Class<?> storageSuperClass, Class<?> factoryInterf
11331204

11341205
@Override
11351206
public void afterImageWrite(AfterImageWriteAccess access) {
1136-
if (SubstrateOptions.TruffleStableOptions.CopyLanguageResources.getValue()) {
1207+
if (copyLanguageResources) {
11371208
Path buildDir = access.getImagePath();
11381209
if (buildDir != null) {
11391210
Path parent = buildDir.getParent();
@@ -1150,14 +1221,13 @@ private void copyResources(Path buildDir) {
11501221
Path languagesDir = resourcesDir.resolve("languages");
11511222
if (Files.exists(languagesDir)) {
11521223
try (Stream<Path> filesToDelete = Files.walk(languagesDir)) {
1153-
filesToDelete.sorted(Comparator.reverseOrder())
1154-
.forEach(f -> {
1155-
try {
1156-
Files.deleteIfExists(f);
1157-
} catch (IOException ioe) {
1158-
throw VMError.shouldNotReachHere("Deletion of previous language resources directory failed.", ioe);
1159-
}
1160-
});
1224+
filesToDelete.sorted(Comparator.reverseOrder()).forEach(f -> {
1225+
try {
1226+
Files.deleteIfExists(f);
1227+
} catch (IOException ioe) {
1228+
throw VMError.shouldNotReachHere("Deletion of previous language resources directory failed.", ioe);
1229+
}
1230+
});
11611231
} catch (IOException ioe) {
11621232
throw VMError.shouldNotReachHere("Deletion of previous language resources directory failed.", ioe);
11631233
}
@@ -1464,20 +1534,6 @@ final class Target_com_oracle_truffle_polyglot_LanguageCache {
14641534
private String languageHome;
14651535
}
14661536

1467-
@TargetClass(className = "com.oracle.truffle.polyglot.InternalResourceCache", onlyWith = TruffleBaseFeature.IsEnabled.class)
1468-
final class Target_com_oracle_truffle_polyglot_InternalResourceCache {
1469-
1470-
@Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = UseInternalResourcesComputer.class, isFinal = true) //
1471-
private static boolean useInternalResources;
1472-
1473-
private static final class UseInternalResourcesComputer implements FieldValueTransformer {
1474-
@Override
1475-
public Object transform(Object receiver, Object originalValue) {
1476-
return SubstrateOptions.TruffleStableOptions.CopyLanguageResources.getValue();
1477-
}
1478-
}
1479-
}
1480-
14811537
@TargetClass(className = "com.oracle.truffle.polyglot.PolyglotEngineImpl", onlyWith = TruffleBaseFeature.IsEnabled.class)
14821538
final class Target_com_oracle_truffle_polyglot_PolyglotEngineImpl {
14831539
@Substitute

truffle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ This changelog summarizes major changes between Truffle versions relevant to lan
3737
* GR-32682 Added `InstrumentableNode.findProbe()` to specify how an instrumentable node finds its associated probe. This is useful for bytecode interpreters to store the probe nodes in a separate data structure without associated wrapper nodes.
3838
* GR-32682 Added `InstrumentableNode.createProbe(SourceSection)` method, which allows to create eager probe nodes for an instrumentable node. Eager probes do not wait for a probe to be inserted and for example all probes for statements can be inserted in a batch. Eager probes are typically used in combination with overriding the `InstrumentableNode.findProbe()` method.
3939
* GR-32682 Added detection of boxing overloads to support state sharing and better boxing elimination. See `Specialization#rewriteOn` for details.
40+
* `TruffleSafepoint#poll(Node)` does not require a non-null location anymore. However, it is still recommended to always pass a location node, if available.
41+
* GR-57838 Added `InternalResource#unpackResourceFiles(Path, Path, Path, Predicate)` to allow filtering of resources to unpack.
4042

4143
## Version 24.1.0
4244
* GR-43839 Added optional parameter to TruffleString.ByteIndexOfCodePointSetNode to choose whether the node may calculate the input string's precise code range.

truffle/mx.truffle/mx_truffle.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,10 @@ def _qualname(distribution_name):
14831483
deps=[],
14841484
layout={
14851485
f'{resource_base_folder}/': f'dependency:{build_library.name}',
1486-
f'{resource_base_folder}/resources': f'dependency:{build_library.name}/resources',
1486+
f'{resource_base_folder}/resources': {"source_type": "dependency",
1487+
"dependency": f'{build_library.name}',
1488+
"path": 'resources',
1489+
"optional": True},
14871490
},
14881491
path=None,
14891492
platformDependent=True,

truffle/src/com.oracle.truffle.api/snapshot.sigtest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ meth public com.oracle.truffle.api.InternalResource$CPUArchitecture getCPUArchit
190190
meth public com.oracle.truffle.api.InternalResource$OS getOS()
191191
meth public java.util.List<java.lang.String> readResourceLines(java.nio.file.Path) throws java.io.IOException
192192
meth public void unpackResourceFiles(java.nio.file.Path,java.nio.file.Path,java.nio.file.Path) throws java.io.IOException
193+
meth public void unpackResourceFiles(java.nio.file.Path,java.nio.file.Path,java.nio.file.Path,java.util.function.Predicate<java.nio.file.Path>) throws java.io.IOException
193194
supr java.lang.Object
194195
hfds contextPreinitializationCheck,owner,resourceClass
195196

0 commit comments

Comments
 (0)