diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 84828f113b6f..a08f7024d878 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -9,6 +9,8 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-38965) Heap dumps are now supported in Community Edition. * (GR-38951) Add `-XX:+DumpHeapAndExit` option to dump the initial heap of a native executable. * (GR-37582) Run image-builder on module-path per default. Opt-out with env setting `USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM=false`. +* (GR-38660) Expose -H:Name= as API option -o +* (GR-39043) Make certain native-image options command-line only and ensure they get processed before other options (--exclude-config --configurations-path --version --help --help-extra --dry-run --debug-attach --expert-options --expert-options-all --expert-options-detail --verbose-server --server-*) ## Version 22.1.0 * (GR-36568) Add "Quick build" mode, enabled through option `-Ob`, for quicker native image builds. 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 9efd164eadd1..4c0995e00032 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 @@ -93,6 +93,7 @@ public static boolean parseOnce() { @Option(help = "Name of the main entry point method. Optional if --shared is used.")// public static final HostedOptionKey Method = new HostedOptionKey<>("main"); + @APIOption(name = "-o", valueSeparator = APIOption.WHITESPACE_SEPARATOR)// @Option(help = "Name of the output file to be generated", type = OptionType.User)// public static final HostedOptionKey Name = new HostedOptionKey<>(""); diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/CmdLineOptionHandler.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/CmdLineOptionHandler.java new file mode 100644 index 000000000000..599822ff6234 --- /dev/null +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/CmdLineOptionHandler.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.driver; + +import java.io.File; +import java.nio.file.Paths; +import java.util.regex.Pattern; + +import org.graalvm.compiler.options.OptionType; + +import com.oracle.svm.core.option.OptionOrigin; +import com.oracle.svm.core.option.OptionUtils; +import com.oracle.svm.driver.NativeImage.ArgumentQueue; + +class CmdLineOptionHandler extends NativeImage.OptionHandler { + + private static final String helpText = NativeImage.getResource("/Help.txt"); + private static final String helpExtraText = NativeImage.getResource("/HelpExtra.txt"); + + /* Defunct legacy options that we have to accept to maintain backward compatibility */ + private static final String verboseServerOption = "--verbose-server"; + private static final String serverOptionPrefix = "--server-"; + + private static final String javaRuntimeVersion = System.getProperty("java.runtime.version"); + + boolean useDebugAttach = false; + + CmdLineOptionHandler(NativeImage nativeImage) { + super(nativeImage); + } + + @Override + boolean consume(ArgumentQueue args) { + String headArg = args.peek(); + boolean consumed = consume(args, headArg); + OptionOrigin origin = OptionOrigin.from(args.argumentOrigin); + if (consumed && !origin.commandLineLike()) { + String msg = String.format("Using '%s' provided by %s is only allowed on command line.", headArg, origin); + throw NativeImage.showError(msg); + } + return consumed; + } + + private boolean consume(ArgumentQueue args, String headArg) { + switch (headArg) { + case "--help": + args.poll(); + singleArgumentCheck(args, headArg); + nativeImage.showMessage(helpText); + nativeImage.showNewline(); + nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage, false); + nativeImage.showNewline(); + nativeImage.optionRegistry.showOptions(null, true, nativeImage::showMessage); + nativeImage.showNewline(); + System.exit(0); + return true; + case "--version": + args.poll(); + singleArgumentCheck(args, headArg); + String message; + if (NativeImage.IS_AOT) { + message = System.getProperty("java.vm.version"); + } else { + message = "native-image " + NativeImage.graalvmVersion + " " + NativeImage.graalvmConfig; + } + message += " (Java Version " + javaRuntimeVersion + ")"; + nativeImage.showMessage(message); + System.exit(0); + return true; + case "--help-extra": + args.poll(); + singleArgumentCheck(args, headArg); + nativeImage.showMessage(helpExtraText); + nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage, true); + nativeImage.showNewline(); + nativeImage.optionRegistry.showOptions(OptionUtils.MacroOptionKind.Macro, true, nativeImage::showMessage); + nativeImage.showNewline(); + System.exit(0); + return true; + case "--configurations-path": + args.poll(); + String configPath = args.poll(); + if (configPath == null) { + NativeImage.showError(headArg + " requires a " + File.pathSeparator + " separated list of directories"); + } + for (String configDir : configPath.split(File.pathSeparator)) { + nativeImage.addMacroOptionRoot(nativeImage.canonicalize(Paths.get(configDir))); + } + return true; + case "--exclude-config": + args.poll(); + String excludeJar = args.poll(); + if (excludeJar == null) { + NativeImage.showError(headArg + " requires two arguments: a jar regular expression and a resource regular expression"); + } + String excludeConfig = args.poll(); + if (excludeConfig == null) { + NativeImage.showError(headArg + " requires resource regular expression"); + } + nativeImage.addExcludeConfig(Pattern.compile(excludeJar), Pattern.compile(excludeConfig)); + return true; + case DefaultOptionHandler.verboseOption: + args.poll(); + nativeImage.setVerbose(true); + return true; + case "--dry-run": + args.poll(); + nativeImage.setDryRun(true); + return true; + case "--expert-options": + args.poll(); + nativeImage.setPrintFlagsOptionQuery(OptionType.User.name()); + return true; + case "--expert-options-all": + args.poll(); + nativeImage.setPrintFlagsOptionQuery(""); + return true; + case "--expert-options-detail": + args.poll(); + String optionNames = args.poll(); + nativeImage.setPrintFlagsWithExtraHelpOptionQuery(optionNames); + return true; + case verboseServerOption: + args.poll(); + NativeImage.showWarning("Ignoring server-mode native-image argument " + headArg + "."); + return true; + } + + String debugAttach = "--debug-attach"; + if (headArg.startsWith(debugAttach)) { + if (useDebugAttach) { + throw NativeImage.showError("The " + debugAttach + " option can only be used once."); + } + useDebugAttach = true; + String debugAttachArg = args.poll(); + String addressSuffix = debugAttachArg.substring(debugAttach.length()); + String address = addressSuffix.isEmpty() ? "8000" : addressSuffix.substring(1); + /* Using agentlib to allow interoperability with other agents */ + nativeImage.addImageBuilderJavaArgs("-agentlib:jdwp=transport=dt_socket,server=y,address=" + address + ",suspend=y"); + /* Disable watchdog mechanism */ + nativeImage.addPlainImageBuilderArg(nativeImage.oHDeadlockWatchdogInterval + "0"); + return true; + } + + if (headArg.startsWith(serverOptionPrefix)) { + args.poll(); + NativeImage.showWarning("Ignoring server-mode native-image argument " + headArg + "."); + String serverOptionCommand = headArg.substring(serverOptionPrefix.length()); + if (!serverOptionCommand.startsWith("session=")) { + /* + * All but the --server-session=... option used to exit(0). We want to simulate that + * behaviour for proper backward compatibility. + */ + System.exit(0); + } + return true; + } + + return false; + } + + private static void singleArgumentCheck(ArgumentQueue args, String arg) { + if (!args.isEmpty()) { + NativeImage.showError("Option " + arg + " cannot be combined with other options."); + } + } +} diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java index 121d431ed9e1..4ab87d118ab3 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java @@ -32,84 +32,32 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; - -import org.graalvm.compiler.options.OptionType; import com.oracle.svm.core.option.OptionOrigin; -import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.driver.NativeImage.ArgumentQueue; class DefaultOptionHandler extends NativeImage.OptionHandler { - private static final String verboseOption = "--verbose"; + static final String verboseOption = "--verbose"; private static final String requireValidJarFileMessage = "-jar requires a valid jarfile"; private static final String newStyleClasspathOptionName = "--class-path"; private static final String addModulesOption = "--add-modules"; private static final String addModulesErrorMessage = " requires modules to be specified"; - static final String helpText = NativeImage.getResource("/Help.txt"); - static final String helpExtraText = NativeImage.getResource("/HelpExtra.txt"); - /* Defunct legacy options that we have to accept to maintain backward compatibility */ - static final String noServerOption = "--no-server"; - static final String verboseServerOption = "--verbose-server"; - static final String serverOptionPrefix = "--server-"; + private static final String noServerOption = "--no-server"; DefaultOptionHandler(NativeImage nativeImage) { super(nativeImage); } - boolean useDebugAttach = false; boolean disableAtFiles = false; - private static void singleArgumentCheck(ArgumentQueue args, String arg) { - if (!args.isEmpty()) { - NativeImage.showError("Option " + arg + " cannot be combined with other options."); - } - } - - private static final String javaRuntimeVersion = System.getProperty("java.runtime.version"); - @Override public boolean consume(ArgumentQueue args) { String headArg = args.peek(); switch (headArg) { - case "--help": - args.poll(); - singleArgumentCheck(args, headArg); - nativeImage.showMessage(helpText); - nativeImage.showNewline(); - nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage, false); - nativeImage.showNewline(); - nativeImage.optionRegistry.showOptions(null, true, nativeImage::showMessage); - nativeImage.showNewline(); - System.exit(0); - return true; - case "--version": - args.poll(); - singleArgumentCheck(args, headArg); - String message; - if (NativeImage.IS_AOT) { - message = System.getProperty("java.vm.version"); - } else { - message = "native-image " + NativeImage.graalvmVersion + " " + NativeImage.graalvmConfig; - } - message += " (Java Version " + javaRuntimeVersion + ")"; - nativeImage.showMessage(message); - System.exit(0); - return true; - case "--help-extra": - args.poll(); - singleArgumentCheck(args, headArg); - nativeImage.showMessage(helpExtraText); - nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage, true); - nativeImage.showNewline(); - nativeImage.optionRegistry.showOptions(OptionUtils.MacroOptionKind.Macro, true, nativeImage::showMessage); - nativeImage.showNewline(); - System.exit(0); - return true; case "-cp": case "-classpath": case newStyleClasspathOptionName: @@ -152,16 +100,6 @@ public boolean consume(ArgumentQueue args) { nativeImage.addImageBuilderJavaArgs(addModulesOption, addModulesArgs); nativeImage.addAddedModules(addModulesArgs); return true; - case "--configurations-path": - args.poll(); - String configPath = args.poll(); - if (configPath == null) { - NativeImage.showError(headArg + " requires a " + File.pathSeparator + " separated list of directories"); - } - for (String configDir : configPath.split(File.pathSeparator)) { - nativeImage.addMacroOptionRoot(nativeImage.canonicalize(Paths.get(configDir))); - } - return true; case "-jar": args.poll(); String jarFilePathStr = args.poll(); @@ -171,44 +109,6 @@ public boolean consume(ArgumentQueue args) { handleJarFileArg(nativeImage.canonicalize(Paths.get(jarFilePathStr))); nativeImage.setJarOptionMode(true); return true; - case verboseOption: - args.poll(); - nativeImage.setVerbose(true); - return true; - case "--dry-run": - args.poll(); - nativeImage.setDryRun(true); - return true; - case "--expert-options": - args.poll(); - nativeImage.setPrintFlagsOptionQuery(OptionType.User.name()); - return true; - case "--expert-options-all": - args.poll(); - nativeImage.setPrintFlagsOptionQuery(""); - return true; - case "--expert-options-detail": - args.poll(); - String optionNames = args.poll(); - nativeImage.setPrintFlagsWithExtraHelpOptionQuery(optionNames); - return true; - case noServerOption: - case verboseServerOption: - args.poll(); - NativeImage.showWarning("Ignoring server-mode native-image argument " + headArg + "."); - return true; - case "--exclude-config": - args.poll(); - String excludeJar = args.poll(); - if (excludeJar == null) { - NativeImage.showError(headArg + " requires two arguments: a jar regular expression and a resource regular expression"); - } - String excludeConfig = args.poll(); - if (excludeConfig == null) { - NativeImage.showError(headArg + " requires resource regular expression"); - } - nativeImage.addExcludeConfig(Pattern.compile(excludeJar), Pattern.compile(excludeConfig)); - return true; case "--diagnostics-mode": args.poll(); nativeImage.setDiagnostics(true); @@ -220,22 +120,10 @@ public boolean consume(ArgumentQueue args) { args.poll(); disableAtFiles = true; return true; - } - - String debugAttach = "--debug-attach"; - if (headArg.startsWith(debugAttach)) { - if (useDebugAttach) { - throw NativeImage.showError("The " + debugAttach + " option can only be used once."); - } - useDebugAttach = true; - String debugAttachArg = args.poll(); - String addressSuffix = debugAttachArg.substring(debugAttach.length()); - String address = addressSuffix.isEmpty() ? "8000" : addressSuffix.substring(1); - /* Using agentlib to allow interoperability with other agents */ - nativeImage.addImageBuilderJavaArgs("-agentlib:jdwp=transport=dt_socket,server=y,address=" + address + ",suspend=y"); - /* Disable watchdog mechanism */ - nativeImage.addPlainImageBuilderArg(nativeImage.oHDeadlockWatchdogInterval + "0"); - return true; + case noServerOption: + args.poll(); + NativeImage.showWarning("Ignoring server-mode native-image argument " + headArg + "."); + return true; } String singleArgClasspathPrefix = newStyleClasspathOptionName + "="; @@ -293,19 +181,6 @@ public boolean consume(ArgumentQueue args) { } return true; } - if (headArg.startsWith(serverOptionPrefix)) { - args.poll(); - NativeImage.showWarning("Ignoring server-mode native-image argument " + headArg + "."); - String serverOptionCommand = headArg.substring(serverOptionPrefix.length()); - if (!serverOptionCommand.startsWith("session=")) { - /* - * All but the --server-session=... option used to exit(0). We want to simulate that - * behaviour for proper backward compatibility. - */ - System.exit(0); - } - return true; - } if (headArg.startsWith(addModulesOption + "=")) { args.poll(); String addModulesArgs = headArg.substring(addModulesOption.length() + 1); 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 63905cad8bd7..f828f3562fa3 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 @@ -185,6 +185,7 @@ void addFallbackBuildArgs(@SuppressWarnings("unused") List buildArgs) { } } + final CmdLineOptionHandler cmdLineOptionHandler; final DefaultOptionHandler defaultOptionHandler; final APIOptionHandler apiOptionHandler; @@ -719,6 +720,8 @@ protected NativeImage(BuildConfiguration config) { /* Discover supported MacroOptions */ optionRegistry = new MacroOption.Registry(); + cmdLineOptionHandler = new CmdLineOptionHandler(this); + /* Default handler needs to be first */ defaultOptionHandler = new DefaultOptionHandler(this); registerOptionHandler(defaultOptionHandler); @@ -1071,7 +1074,7 @@ private int completeImageBuild() { imageBuilderArgs.add(oH(SubstrateOptions.Name, "image-name from module-name") + mainClassModule.toLowerCase()); } else { /* Although very unlikely, report missing image-name if needed. */ - throw showError("Missing image-name. Use " + oHName + " to provide one."); + throw showError("Missing image-name. Use -o to provide one."); } } } @@ -1474,23 +1477,34 @@ public void accept(String arg) { } List apply(boolean strict) { - List leftoverArgs = new ArrayList<>(); + + ArgumentQueue queue = new ArgumentQueue(args.argumentOrigin); while (!args.isEmpty()) { + int numArgs = args.size(); + if (cmdLineOptionHandler.consume(args)) { + assert args.size() < numArgs : "OptionHandler pretends to consume argument(s) but isn't: " + cmdLineOptionHandler.getClass().getName(); + } else { + queue.add(args.poll()); + } + } + + List leftoverArgs = new ArrayList<>(); + while (!queue.isEmpty()) { boolean consumed = false; for (int index = optionHandlers.size() - 1; index >= 0; --index) { OptionHandler handler = optionHandlers.get(index); - int numArgs = args.size(); - if (handler.consume(args)) { - assert args.size() < numArgs : "OptionHandler pretends to consume argument(s) but isn't: " + handler.getClass().getName(); + int numArgs = queue.size(); + if (handler.consume(queue)) { + assert queue.size() < numArgs : "OptionHandler pretends to consume argument(s) but isn't: " + handler.getClass().getName(); consumed = true; break; } } if (!consumed) { if (strict) { - showError("Property 'Args' contains invalid entry '" + args.peek() + "'"); + showError("Property 'Args' contains invalid entry '" + queue.peek() + "'"); } else { - leftoverArgs.add(args.poll()); + leftoverArgs.add(queue.poll()); } } } @@ -1631,7 +1645,7 @@ boolean isDiagnostics() { } boolean useDebugAttach() { - return defaultOptionHandler.useDebugAttach; + return cmdLineOptionHandler.useDebugAttach; } protected void setDryRun(boolean val) {