From 0c42d5920d2997cadfa72f142d5808bc837a19e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 8 Jan 2025 18:19:12 +0100 Subject: [PATCH] Detect unrecognized hosted options in NI driver --- .../graal/compiler/options/OptionsParser.java | 23 ++- substratevm/CHANGELOG.md | 1 + .../mx.substratevm/macro-svmjdwp.properties | 9 ++ .../mx.substratevm/macro-truffle.properties | 11 ++ substratevm/mx.substratevm/mx_substratevm.py | 19 ++- substratevm/mx.substratevm/suite.py | 8 + .../svm/common/option/CommonOptionParser.java | 9 +- .../oracle/svm/core/option/OptionOrigin.java | 7 +- .../oracle/svm/driver/APIOptionHandler.java | 140 +++++++++++++----- .../oracle/svm/driver/MacroOptionHandler.java | 2 + .../com/oracle/svm/driver/NativeImage.java | 13 +- .../native-image.properties | 4 +- .../native-image/native-image.properties | 6 + .../truffle-runtime/native-image.properties | 11 ++ 14 files changed, 212 insertions(+), 51 deletions(-) create mode 100644 substratevm/mx.substratevm/macro-svmjdwp.properties diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/options/OptionsParser.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/options/OptionsParser.java index e6980336fe20..cd8d3fde8ff2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/options/OptionsParser.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/options/OptionsParser.java @@ -36,13 +36,15 @@ import java.util.Objects; import java.util.ServiceLoader; import java.util.Set; +import java.util.function.Function; import java.util.regex.Pattern; -import jdk.graal.compiler.debug.GraalError; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.MapCursor; +import jdk.graal.compiler.debug.GraalError; + /** * This class contains methods for parsing Graal options and matching them against a set of * {@link OptionDescriptors}. The {@link OptionDescriptors} are loaded via a {@link ServiceLoader}. @@ -345,12 +347,25 @@ private static List fuzzyMatch(Iterable loa * @return whether any fuzzy matches were found */ public static boolean collectFuzzyMatches(Iterable toSearch, String name, Collection matches) { + return collectFuzzyMatches(toSearch, name, matches, OptionDescriptor::getName); + } + + /** + * Collects from given entries toSearch the ones that fuzzy match a given String name. String + * similarity for fuzzy matching is based on Dice's coefficient. + * + * @param toSearch the entries search + * @param name the name to search for + * @param matches the collection to which fuzzy matches of {@code name} will be added + * @return whether any fuzzy matches were found + */ + public static boolean collectFuzzyMatches(Iterable toSearch, String name, Collection matches, Function extractor) { boolean found = false; - for (OptionDescriptor option : toSearch) { - float score = stringSimilarity(option.getName(), name); + for (T entry : toSearch) { + float score = stringSimilarity(extractor.apply(entry), name); if (score >= FUZZY_MATCH_THRESHOLD) { found = true; - matches.add(option); + matches.add(entry); } } return found; diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 6b8cca4ef309..a33a50ea6bef 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -7,6 +7,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-59313) Deprecated class-level metadata extraction using `native-image-inspect` and removed option `DumpMethodsData`. Use class-level SBOMs instead by passing `--enable-sbom=class-level,export` to the `native-image` builder. The default value of option `IncludeMethodData` was changed to `false`. * (GR-52400) The build process now uses 85% of system memory in containers and CI environments. Otherwise, it tries to only use available memory. If less than 8GB of memory are available, it falls back to 85% of system memory. The reason for the selected memory limit is now also shown in the build resources section of the build output. * (GR-59864) Added JVM version check to the Native Image agent. The agent will abort execution if the JVM major version does not match the version it was built with, and warn if the full JVM version is different. +* (GR-59135) Verify if hosted options passed to `native-image` exist prior to starting the builder. Provide suggestions how to fix unknown options early on. ## GraalVM for JDK 24 (Internal Version 24.2.0) * (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning. diff --git a/substratevm/mx.substratevm/macro-svmjdwp.properties b/substratevm/mx.substratevm/macro-svmjdwp.properties new file mode 100644 index 000000000000..089d18bcb43d --- /dev/null +++ b/substratevm/mx.substratevm/macro-svmjdwp.properties @@ -0,0 +1,9 @@ +# This file contains support for building images with JDWP debugging support + +ProvidedHostedOptions = JDWP CopyNativeJDWPLibrary + +ImageBuilderModulePath = ${.}/builder/svm-jdwp-common.jar:${.}/builder/svm-jdwp-resident.jar + +Args = -H:+UnlockExperimentalVMOptions \ + -H:+JDWP \ + -H:-UnlockExperimentalVMOptions diff --git a/substratevm/mx.substratevm/macro-truffle.properties b/substratevm/mx.substratevm/macro-truffle.properties index cb8c47999dd2..89a148cb23b8 100644 --- a/substratevm/mx.substratevm/macro-truffle.properties +++ b/substratevm/mx.substratevm/macro-truffle.properties @@ -49,3 +49,14 @@ JavaArgs = -Dtruffle.TruffleRuntime=com.oracle.svm.truffle.api.SubstrateTruffleR --add-exports org.graalvm.truffle/com.oracle.truffle.object=ALL-UNNAMED \ --add-exports org.graalvm.truffle/com.oracle.truffle.object.basic=ALL-UNNAMED \ --add-exports org.graalvm.truffle/com.oracle.truffle.polyglot=ALL-UNNAMED + +ProvidedHostedOptions = \ + PrintStaticTruffleBoundaries \ + TruffleCheckNeverPartOfCompilation \ + TruffleCheckFrameImplementation \ + TruffleCheckBlackListedMethods \ + TruffleCheckBlockListMethods \ + TruffleInlineDuringParsing \ + TruffleCheckPreinitializedFiles \ + TruffleMultiThreaded \ + TrufflePropagateCompilationErrors diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 9daccec5a712..ed091576bda7 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1620,6 +1620,20 @@ def prevent_build_path_in_libgraal(): "-H:+PreserveFramePointer", ] +mx_sdk_vm.register_graalvm_component(mx_sdk_vm.GraalVMSvmMacro( + suite=suite, + name='SubstrateVM JDWP Debugger Resident', + short_name='svmjdwp', + dir_name="svmjdwp", + license_files=[], + third_party_license_files=[], + dependencies=['SubstrateVM'], + builder_jar_distributions=['substratevm:SVM_JDWP_COMMON', 'substratevm:SVM_JDWP_RESIDENT'], + support_distributions=['substratevm:SVM_JDWP_RESIDENT_SUPPORT'], + stability="experimental", + jlink=False, +)) + libsvmjdwp_lib_config = mx_sdk_vm.LibraryConfig( destination="", jvm_library=True, @@ -1634,19 +1648,20 @@ def prevent_build_path_in_libgraal(): libsvmjdwp = mx_sdk_vm.GraalVmJreComponent( suite=suite, name='SubstrateVM JDWP Debugger', - short_name='svmjdwp', + short_name='svmjdwpserver', dir_name="svm", license_files=[], third_party_license_files=[], dependencies=[], jar_distributions=[], - builder_jar_distributions=['substratevm:SVM_JDWP_COMMON', 'substratevm:SVM_JDWP_RESIDENT'], + builder_jar_distributions=[], support_distributions=[], priority=1, library_configs=[libsvmjdwp_lib_config], stability="experimental", jlink=False, ) + mx_sdk_vm.register_graalvm_component(libsvmjdwp) def _native_image_configure_extra_jvm_args(): diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 52097aef67d9..41dbc04a8284 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -2591,6 +2591,14 @@ } }, + "SVM_JDWP_RESIDENT_SUPPORT" : { + "native" : True, + "description" : "JDWP debugging support", + "layout" : { + "native-image.properties" : "file:mx.substratevm/macro-svmjdwp.properties", + }, + }, + "SVM_JDWP_SERVER": { "subDir": "src", "dependencies": [ diff --git a/substratevm/src/com.oracle.svm.common/src/com/oracle/svm/common/option/CommonOptionParser.java b/substratevm/src/com.oracle.svm.common/src/com/oracle/svm/common/option/CommonOptionParser.java index 98efb5ee0afd..f143a2aeb656 100644 --- a/substratevm/src/com.oracle.svm.common/src/com/oracle/svm/common/option/CommonOptionParser.java +++ b/substratevm/src/com.oracle.svm.common/src/com/oracle/svm/common/option/CommonOptionParser.java @@ -58,6 +58,9 @@ public class CommonOptionParser { @Platforms(Platform.HOSTED_ONLY.class) // public static final String HOSTED_OPTION_PREFIX = "-H:"; public static final String RUNTIME_OPTION_PREFIX = "-R:"; + public static final char PLUS_MINUS_BOOLEAN_OPTION_PREFIX = '\u00b1'; + public static final String MISMATCH_BOOLEAN_OPTION = "Boolean option %s must have " + PLUS_MINUS_BOOLEAN_OPTION_PREFIX + " prefix. Use '" + PLUS_MINUS_BOOLEAN_OPTION_PREFIX + "%s' format."; + public static final String MISMATCH_NON_BOOLEAN_OPTION = "Non-boolean option %s can not use " + PLUS_MINUS_BOOLEAN_OPTION_PREFIX + " prefix. Use '%s=' format."; public static final int PRINT_OPTION_INDENTATION = 2; public static final int PRINT_OPTION_WIDTH = 45; @@ -243,7 +246,7 @@ public static OptionParseResult parseOption(EconomicMap' format"); + return OptionParseResult.error(MISMATCH_NON_BOOLEAN_OPTION.formatted(current, current.name)); } } @@ -546,7 +549,7 @@ public static void printFlags(Predicate filter, EconomicMap { private static final String ENTER_UNLOCK_SCOPE = SubstrateOptionsParser.commandArgument(SubstrateOptions.UnlockExperimentalVMOptions, "+"); @@ -83,20 +86,20 @@ boolean isDeprecated() { } } - static final class PathsOptionInfo { - private final String delimiter; - private final BundleMember.Role role; - - PathsOptionInfo(String delimiter, BundleMember.Role role) { - this.delimiter = delimiter; - this.role = role; - } + record PathsOptionInfo(String delimiter, BundleMember.Role role) { } private final SortedMap apiOptions; private final Map groupInfos; private final Map pathOptions; - private final Set stableOptionNames; + + record HostedOptionInfo(Boolean isStable, Boolean isBoolean) { + } + + private final HostedOptionInfo injectedKnownHostedRegularOptionInfo = new HostedOptionInfo(false, false); + private final HostedOptionInfo injectedKnownHostedBooleanOptionInfo = new HostedOptionInfo(false, true); + + private final Map allOptionNames; private int numberOfActiveUnlockExperimentalVMOptions = 0; private Set illegalExperimentalOptions = new HashSet<>(0); @@ -107,28 +110,28 @@ static final class PathsOptionInfo { APIOptionSupport support = ImageSingletons.lookup(APIOptionSupport.class); groupInfos = support.groupInfos(); pathOptions = support.pathOptions(); - stableOptionNames = support.stableOptionNames(); + allOptionNames = support.allOptionNames(); apiOptions = support.options(); } else { groupInfos = new HashMap<>(); pathOptions = new HashMap<>(); - stableOptionNames = new HashSet<>(); - apiOptions = extractOptions(ServiceLoader.load(OptionDescriptors.class, nativeImage.getClass().getClassLoader()), groupInfos, pathOptions, stableOptionNames); + allOptionNames = new HashMap<>(); + apiOptions = extractOptions(ServiceLoader.load(OptionDescriptors.class, nativeImage.getClass().getClassLoader()), groupInfos, pathOptions, allOptionNames); } } static SortedMap extractOptions(ServiceLoader optionDescriptors, Map groupInfos, Map pathOptions, - Set stableOptionNames) { + Map allOptionNames) { EconomicMap hostedOptions = EconomicMap.create(); EconomicMap runtimeOptions = EconomicMap.create(); HostedOptionParser.collectOptions(optionDescriptors, hostedOptions, runtimeOptions); SortedMap apiOptions = new TreeMap<>(); Map, APIOptionGroup> groupInstances = new HashMap<>(); hostedOptions.getValues().forEach(o -> { - extractOption(NativeImage.oH, o, apiOptions, groupInfos, groupInstances, stableOptionNames); + extractOption(NativeImage.oH, o, apiOptions, groupInfos, groupInstances, allOptionNames); extractPathOption(NativeImage.oH, o, pathOptions); }); - runtimeOptions.getValues().forEach(o -> extractOption(NativeImage.oR, o, apiOptions, groupInfos, groupInstances, stableOptionNames)); + runtimeOptions.getValues().forEach(o -> extractOption(NativeImage.oR, o, apiOptions, groupInfos, groupInstances, allOptionNames)); groupInfos.forEach((groupName, groupInfo) -> { if (groupInfo.defaultValues.size() > 1) { VMError.shouldNotReachHere(String.format("APIOptionGroup %s must only have a single default (but has: %s)", @@ -139,7 +142,11 @@ static SortedMap extractOptions(ServiceLoader apiOptions, - Map groupInfos, Map, APIOptionGroup> groupInstances, Set stableOptionNames) { + Map groupInfos, Map, APIOptionGroup> groupInstances, Map allOptionNames) { + + Class optionValueType = optionDescriptor.getOptionValueType(); + boolean isBooleanOption = optionValueType.equals(Boolean.class); + for (APIOption apiAnnotation : OptionUtils.getAnnotationsByType(optionDescriptor, APIOption.class)) { String builderOption = optionPrefix; if (apiAnnotation.name().length <= 0) { @@ -150,14 +157,12 @@ private static void extractOption(String optionPrefix, OptionDescriptor optionDe APIOptionGroup group = null; String defaultValue = null; - boolean booleanOption = false; - Class optionValueType = optionDescriptor.getOptionValueType(); if (optionValueType.isArray()) { VMError.guarantee(optionDescriptor.getOptionKey() instanceof HostedOptionKey, "Only HostedOptionKeys are allowed to have array type key values."); optionValueType = optionValueType.getComponentType(); } boolean hasFixedValue = apiAnnotation.fixedValue().length > 0; - if (optionValueType.equals(Boolean.class)) { + if (isBooleanOption) { if (!apiAnnotation.group().equals(APIOption.NullGroup.class)) { try { Class groupClass = apiAnnotation.group(); @@ -191,7 +196,6 @@ private static void extractOption(String optionPrefix, OptionDescriptor optionDe } builderOption += apiAnnotation.kind().equals(APIOptionKind.Negated) ? "-" : "+"; builderOption += rawOptionName; - booleanOption = true; } else { if (!apiAnnotation.group().equals(APIOption.NullGroup.class)) { VMError.shouldNotReachHere(String.format("Using @APIOption.group not supported for non-boolean APIOption %s(%s)", apiOptionName, rawOptionName)); @@ -246,7 +250,7 @@ private static void extractOption(String optionPrefix, OptionDescriptor optionDe for (char valueSeparator : apiAnnotation.valueSeparator()) { if (valueSeparator == APIOption.WHITESPACE_SEPARATOR) { String msgTail = " cannot use APIOption.WHITESPACE_SEPARATOR as value separator"; - if (booleanOption) { + if (isBooleanOption) { throw VMError.shouldNotReachHere(String.format("Boolean APIOption %s(%s)" + msgTail, apiOptionName, rawOptionName)); } if (hasFixedValue) { @@ -257,16 +261,13 @@ private static void extractOption(String optionPrefix, OptionDescriptor optionDe } } } - boolean defaultFinal = booleanOption || hasFixedValue; + boolean defaultFinal = isBooleanOption || hasFixedValue; apiOptions.put(apiOptionName, new APIOptionHandler.OptionInfo(apiAnnotation.name(), apiAnnotation.valueSeparator(), builderOption, defaultValue, helpText, defaultFinal, apiAnnotation.deprecated(), valueTransformers, group, apiAnnotation.extra(), apiAnnotation.launcherOption())); } - if (optionDescriptor.getStability() == OptionStability.STABLE) { - String infix = optionDescriptor.getOptionValueType() == Boolean.class ? "[+-]+" : ""; - stableOptionNames.add("^-H:" + infix + optionDescriptor.getName() + ".*"); - } + allOptionNames.put(optionDescriptor.getName(), new HostedOptionInfo(optionDescriptor.getStability() == OptionStability.STABLE, isBooleanOption)); } private static void extractPathOption(String optionPrefix, OptionDescriptor optionDescriptor, Map pathOptions) { @@ -289,6 +290,19 @@ private static String startLowerCase(String str) { return str.substring(0, 1).toLowerCase(Locale.ROOT) + str.substring(1); } + void injectKnownHostedOption(String optionName) { + String baseOptionName; + HostedOptionInfo optionInfo; + if (optionName.endsWith("=")) { + baseOptionName = optionName.substring(0, optionName.length() - 1); + optionInfo = injectedKnownHostedRegularOptionInfo; + } else { + baseOptionName = optionName; + optionInfo = injectedKnownHostedBooleanOptionInfo; + } + allOptionNames.put(baseOptionName, optionInfo); + } + @Override boolean consume(ArgumentQueue args) { String headArg = args.peek(); @@ -312,9 +326,8 @@ boolean consume(ArgumentQueue args) { throw VMError.shouldNotReachHere("Unlocking of experimental options in inconsistent state: trying to lock more scopes than exist or allowed."); } numberOfActiveUnlockExperimentalVMOptions--; - } else if (numberOfActiveUnlockExperimentalVMOptions == 0 && !OptionOrigin.isAPI(args.argumentOrigin) && headArg.startsWith(NativeImage.oH) && - stableOptionNames.stream().noneMatch(p -> headArg.matches(p))) { - illegalExperimentalOptions.add(headArg); + } else if (!OptionOrigin.isAPI(args.argumentOrigin) && headArg.startsWith(NativeImage.oH)) { + validateHostedOption(headArg, args.argumentOrigin); } for (Entry entry : groupInfos.entrySet()) { String groupNameAndSeparator = entry.getKey(); @@ -328,6 +341,67 @@ boolean consume(ArgumentQueue args) { return false; } + private void validateHostedOption(String hostedOptionArg, String argumentOrigin) { + String optionName = hostedOptionArg.substring(NativeImage.oH.length()).split("=", 2)[0].split("@", 2)[0]; + char booleanPrefix = 0; + if (!optionName.isEmpty()) { + char first = optionName.charAt(0); + if (first == '+' || first == '-') { + optionName = optionName.substring(1); + booleanPrefix = first; + } + } + HostedOptionInfo info = allOptionNames.get(optionName); + if (info == null) { + List matches = new ArrayList<>(); + OptionsParser.collectFuzzyMatches(() -> allOptionNames.keySet().iterator(), optionName, matches, Function.identity()); + StringBuilder msg = new StringBuilder("Unrecognized option "); + msg.append(optionDescription(optionName, booleanPrefix, argumentOrigin)).append('.'); + if (!matches.isEmpty()) { + msg.append(" Did you mean one of these:"); + for (var match : matches) { + msg.append(' ').append('\'').append(NativeImage.oH); + boolean matchIsBoolean = allOptionNames.get(match).isBoolean; + if (matchIsBoolean) { + msg.append(CommonOptionParser.PLUS_MINUS_BOOLEAN_OPTION_PREFIX); + } + msg.append(match); + if (!matchIsBoolean) { + msg.append("=..."); + } + msg.append('\''); + } + msg.append('.'); + } + msg.append(" Use '--expert-options' (see also '--help-extra') to list all available options."); + throw NativeImage.showError(msg.toString()); + } + if ((booleanPrefix != 0) != info.isBoolean()) { + var optionDescription = optionDescription(optionName, booleanPrefix, argumentOrigin); + if (info.isBoolean()) { + throw NativeImage.showError(CommonOptionParser.MISMATCH_BOOLEAN_OPTION.formatted(optionDescription, optionName)); + } else { + throw NativeImage.showError(CommonOptionParser.MISMATCH_NON_BOOLEAN_OPTION.formatted(optionDescription, optionName)); + } + } + if (numberOfActiveUnlockExperimentalVMOptions == 0 && !info.isStable()) { + illegalExperimentalOptions.add(hostedOptionArg); + } + } + + private static String optionDescription(String optionName, char booleanPrefix, String argumentOrigin) { + var result = new StringBuilder("'" + NativeImage.oH); + if (booleanPrefix != 0) { + result.append(booleanPrefix); + } + result.append(optionName); + if (booleanPrefix == 0) { + result.append("=..."); + } + result.append("' from ").append(OptionOrigin.from(argumentOrigin)); + return result.toString(); + } + String translateOption(ArgumentQueue argQueue) { OptionInfo option = null; boolean whitespaceSeparated = false; @@ -622,7 +696,7 @@ class GroupInfo { } record APIOptionSupport(Map groupInfos, SortedMap options, Map pathOptions, - Set stableOptionNames) { + Map allOptionNames) { } final class APIOptionFeature implements Feature { @@ -638,9 +712,9 @@ public void duringSetup(DuringSetupAccess access) { FeatureImpl.DuringSetupAccessImpl accessImpl = (FeatureImpl.DuringSetupAccessImpl) access; Map groupInfos = new HashMap<>(); Map pathOptions = new HashMap<>(); - Set stableOptionNames = new HashSet<>(); + Map allOptionNames = new HashMap<>(); ServiceLoader optionDescriptors = ServiceLoader.load(OptionDescriptors.class, accessImpl.getImageClassLoader().getClassLoader()); - SortedMap options = APIOptionHandler.extractOptions(optionDescriptors, groupInfos, pathOptions, stableOptionNames); - ImageSingletons.add(APIOptionSupport.class, new APIOptionSupport(groupInfos, options, pathOptions, stableOptionNames)); + SortedMap options = APIOptionHandler.extractOptions(optionDescriptors, groupInfos, pathOptions, allOptionNames); + ImageSingletons.add(APIOptionSupport.class, new APIOptionSupport(groupInfos, options, pathOptions, allOptionNames)); } } diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java index 2c25d8c75654..82ca4038558d 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java @@ -97,6 +97,8 @@ private void applyEnabled(MacroOption.EnabledOption enabledOption, String argume config.modulePathBuild = modulePathBuild; } + enabledOption.forEachPropertyValue(config, + "ProvidedHostedOptions", nativeImage.apiOptionHandler::injectKnownHostedOption, NativeImage.MANY_SPACES_REGEX); enabledOption.forEachPropertyValue(config, "ImageProvidedJars", entry -> nativeImage.addImageProvidedJars(Path.of(entry)), PATH_SEPARATOR_REGEX); enabledOption.forEachPropertyValue(config, 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 0abe93bbf52b..0b7eb0fea53c 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 @@ -779,6 +779,7 @@ public boolean processMetaInfResource(Path classpathEntry, Path resourceRoot, Pa } forEachPropertyValue(properties.get("JavaArgs"), NativeImage.this::addImageBuilderJavaArgs, resolver); forEachPropertyValue(properties.get("Args"), args, resolver); + forEachPropertyValue(properties.get("ProvidedHostedOptions"), apiOptionHandler::injectKnownHostedOption, resolver); } else { args.accept(oH(type.optionKey) + resourceRoot.relativize(resourcePath)); } @@ -1058,12 +1059,12 @@ private static String injectHostedOptionOrigin(String option, String origin) { char boolPrefix = option.length() > oH.length() ? option.charAt(oH.length()) : 0; if (boolPrefix == '-' || boolPrefix == '+') { if (eqIndex != -1) { - showError("Invalid boolean native-image hosted-option " + option + " at " + origin); + showError("Malformed boolean native-image hosted-option '" + option + "' (boolean option with extraneous '=') from " + OptionOrigin.from(origin) + "."); } return option + optionOriginSeparator + origin; } else { if (eqIndex == -1) { - showError("Invalid native-image hosted-option " + option + " at " + origin); + showError("Malformed native-image hosted-option '" + option + "' ('=' missing after option name) from " + OptionOrigin.from(origin) + "."); } String front = option.substring(0, eqIndex); String back = option.substring(eqIndex); @@ -1978,14 +1979,14 @@ private static void sanitizeJVMEnvironment(Map environment, Map< environment.putAll(restrictedEnvironment); } - private static final Function defaultNativeImageProvider = NativeImage::new; + protected static Function defaultNativeImageProvider = NativeImage::new; public static void main(String[] args) { performBuild(new BuildConfiguration(Arrays.asList(args)), defaultNativeImageProvider); } public static List translateAPIOptions(List arguments) { - var handler = new APIOptionHandler(new NativeImage(new BuildConfiguration(arguments))); + var handler = new APIOptionHandler(defaultNativeImageProvider.apply(new BuildConfiguration(arguments))); var argumentQueue = new ArgumentQueue(OptionOrigin.originDriver); handler.nativeImage.config.args.forEach(argumentQueue::add); List translatedOptions = new ArrayList<>(); @@ -2558,8 +2559,10 @@ private boolean configureBuildOutput() { return useColorfulOutput; } + static final String MANY_SPACES_REGEX = "\\s+"; + static boolean forEachPropertyValue(String propertyValue, Consumer target, Function resolver) { - return forEachPropertyValue(propertyValue, target, resolver, "\\s+"); + return forEachPropertyValue(propertyValue, target, resolver, MANY_SPACES_REGEX); } static boolean forEachPropertyValue(String propertyValue, Consumer target, Function resolver, String separatorRegex) { diff --git a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties index d06f997c45b8..353286208939 100644 --- a/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties +++ b/substratevm/src/com.oracle.svm.junit/resources/META-INF/native-image/org.graalvm.nativeimage.junitsupport/com.oracle.svm.junit/native-image.properties @@ -1,2 +1,4 @@ Args = --add-reads=org.graalvm.nativeimage.junitsupport=ALL-UNNAMED \ - --link-at-build-time=com.oracle.svm.junit \ No newline at end of file + --link-at-build-time=com.oracle.svm.junit + +ProvidedHostedOptions = TestFile= diff --git a/substratevm/src/com.oracle.svm.truffle.tck/src/META-INF/native-image/native-image.properties b/substratevm/src/com.oracle.svm.truffle.tck/src/META-INF/native-image/native-image.properties index 402dd88a938b..65a93d9a4831 100644 --- a/substratevm/src/com.oracle.svm.truffle.tck/src/META-INF/native-image/native-image.properties +++ b/substratevm/src/com.oracle.svm.truffle.tck/src/META-INF/native-image/native-image.properties @@ -18,3 +18,9 @@ JavaArgs = --add-exports org.graalvm.nativeimage.base/com.oracle.svm.util=ALL-UN --add-exports jdk.graal.compiler/jdk.graal.compiler.util.json=ALL-UNNAMED \ --add-exports jdk.internal.vm.ci/jdk.vm.ci.common=ALL-UNNAMED \ --add-exports jdk.internal.vm.ci/jdk.vm.ci.meta=ALL-UNNAMED + +ProvidedHostedOptions = \ + TruffleTCKPermissionsReportFile= \ + TruffleTCKPermissionsExcludeFiles= \ + TruffleTCKPermissionsMaxStackTraceDepth= \ + TruffleTCKPermissionsMaxErrors= diff --git a/truffle/src/com.oracle.truffle.runtime/src/META-INF/native-image/org.graalvm.truffle/truffle-runtime/native-image.properties b/truffle/src/com.oracle.truffle.runtime/src/META-INF/native-image/org.graalvm.truffle/truffle-runtime/native-image.properties index 98a9c51f9fb0..29978d3da4f3 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/META-INF/native-image/org.graalvm.truffle/truffle-runtime/native-image.properties +++ b/truffle/src/com.oracle.truffle.runtime/src/META-INF/native-image/org.graalvm.truffle/truffle-runtime/native-image.properties @@ -6,3 +6,14 @@ Args = --macro:truffle-svm \ JavaArgs = -Dtruffle.TruffleRuntime=com.oracle.svm.truffle.api.SubstrateTruffleRuntime \ -Dgraalvm.ForcePolyglotInvalid=false + +ProvidedHostedOptions = \ + PrintStaticTruffleBoundaries \ + TruffleCheckNeverPartOfCompilation \ + TruffleCheckFrameImplementation \ + TruffleCheckBlackListedMethods \ + TruffleCheckBlockListMethods \ + TruffleInlineDuringParsing \ + TruffleCheckPreinitializedFiles \ + TruffleMultiThreaded \ + TrufflePropagateCompilationErrors