From 80d5035c1c25bbf6a17f7e235455559b27d8b5ab Mon Sep 17 00:00:00 2001 From: Lukas Stadler <lukas.stadler@oracle.com> Date: Wed, 12 Jul 2017 17:44:21 +0200 Subject: [PATCH] parse options, fix to Rscript args parsing, don't echo input in Rscript --- .../r/launcher/JLineConsoleHandler.java | 2 +- .../oracle/truffle/r/launcher/Launcher.java | 514 ++++++++++++++++++ .../truffle/r/launcher/RCmdOptions.java | 19 +- .../oracle/truffle/r/launcher/RCommand.java | 81 ++- .../truffle/r/launcher/RscriptCommand.java | 52 +- com.oracle.truffle.r.native/run/Makefile | 2 +- 6 files changed, 627 insertions(+), 43 deletions(-) create mode 100644 com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java index 1f0ad295e3..fc2f1e2e3b 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java @@ -75,7 +75,7 @@ public class JLineConsoleHandler extends ConsoleHandler { @Override public void setPrompt(String prompt) { - console.setPrompt(noPrompt ? "" : prompt); + console.setPrompt(noPrompt ? "" : prompt != null ? prompt : ""); } public void clearHistory() { diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java new file mode 100644 index 0000000000..cca67ddb07 --- /dev/null +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2017, 2017, 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. + * + * 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.truffle.r.launcher; + +import static java.lang.Integer.max; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.graalvm.options.OptionCategory; +import org.graalvm.options.OptionDescriptor; +import org.graalvm.options.OptionDescriptors; +import org.graalvm.polyglot.Engine; +import org.graalvm.polyglot.Instrument; +import org.graalvm.polyglot.Language; + +public abstract class Launcher { + private static final boolean STATIC_VERBOSE = Boolean.getBoolean("org.graalvm.launcher.verbose"); + + private Engine tempEngine; + + private final boolean verbose; + + private boolean help; + private boolean helpDebug; + private boolean helpExpert; + private boolean helpTools; + private boolean helpLanguages; + + private OptionCategory helpCategory; + private VersionAction versionAction = VersionAction.None; + + protected enum VersionAction { + None, + PrintAndExit, + PrintAndContinue + } + + public Launcher() { + verbose = STATIC_VERBOSE || Boolean.valueOf(System.getenv("VERBOSE_GRAALVM_LAUNCHERS")); + } + + private Engine getTempEngine() { + if (tempEngine == null) { + tempEngine = Engine.create(); + } + return tempEngine; + } + + void setHelpCategory(OptionCategory helpCategory) { + this.helpCategory = helpCategory; + } + + protected void setVersionAction(VersionAction versionAction) { + this.versionAction = versionAction; + } + + private static class AbortException extends RuntimeException { + static final long serialVersionUID = 4681646279864737876L; + + AbortException(String message) { + super(message, null); + } + + @SuppressWarnings("sync-override") + @Override + public final Throwable fillInStackTrace() { + return null; + } + } + + protected AbortException exit() { + return abort(null, 0); + } + + protected AbortException abort(String message) { + return abort(message, 1); + } + + protected AbortException abort(String message, int exitCode) { + if (message != null) { + System.err.println("ERROR: " + message); + } + System.exit(exitCode); + throw new AbortException(message); + } + + protected abstract void printHelp(OptionCategory maxCategory); + + protected abstract void printVersion(); + + protected abstract void collectArguments(Set<String> options); + + protected static void printPolyglotVersions() { + Engine engine = Engine.create(); + System.out.println("GraalVM Polyglot Engine Version " + engine.getVersion()); + printLanguages(engine, true); + printInstruments(engine, true); + } + + protected boolean isVerbose() { + return verbose; + } + + final boolean runPolyglotAction() { + OptionCategory maxCategory = helpDebug ? OptionCategory.DEBUG : (helpExpert ? OptionCategory.EXPERT : (helpCategory != null ? helpCategory : OptionCategory.USER)); + + switch (versionAction) { + case PrintAndContinue: + printVersion(); + return false; + case PrintAndExit: + printVersion(); + return true; + case None: + break; + } + boolean printDefaultHelp = helpCategory != null || help || ((helpExpert || helpDebug) && !helpTools && !helpLanguages); + Engine tmpEngine = null; + if (printDefaultHelp) { + printHelp(maxCategory); + // @formatter:off + System.out.println(); + System.out.println("Runtime Options:"); + printOption("--polyglot", "Run with all other guest languages accessible."); + printOption("--native", "Run using the native launcher with limited Java access (default)."); + printOption("--native.[option]", "Pass options to the native image; for example, '--native.Xmx1G'. To see available options, use '--native.help'."); + printOption("--jvm", "Run on the Java Virtual Machine with Java access."); + printOption("--jvm.[option]", "Pass options to the JVM; for example, '--jvm.classpath=myapp.jar'. To see available options. use '--jvm.help'."); + printOption("--help", "Print this help message."); + printOption("--help:languages", "Print options for all installed languages."); + printOption("--help:tools", "Print options for all installed tools."); + printOption("--help:expert", "Print additional engine options for experts."); + if (helpExpert || helpDebug) { + printOption("--help:debug", "Print additional engine options for debugging."); + } + printOption("--version", "Print version information and exit."); + printOption("--show-version", "Print version information and continue execution."); + // @formatter:on + if (tmpEngine == null) { + tmpEngine = Engine.create(); + } + List<PrintableOption> engineOptions = new ArrayList<>(); + for (OptionDescriptor descriptor : tmpEngine.getOptions()) { + if (!descriptor.getName().startsWith("engine.") && !descriptor.getName().startsWith("compiler.")) { + continue; + } + if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { + engineOptions.add(asPrintableOption(descriptor)); + } + } + if (!engineOptions.isEmpty()) { + printOptions(engineOptions, "Engine options:", 2); + } + } + + if (helpLanguages) { + if (tmpEngine == null) { + tmpEngine = Engine.create(); + } + printLanguageOptions(tmpEngine, maxCategory); + } + + if (helpTools) { + if (tmpEngine == null) { + tmpEngine = Engine.create(); + } + printInstrumentOptions(tmpEngine, maxCategory); + } + + if (printDefaultHelp || helpLanguages || helpTools) { + System.out.println("\nSee http://www.oracle.com/technetwork/oracle-labs/program-languages/overview/index.html for more information."); + return true; + } + + return false; + } + + private static void printInstrumentOptions(Engine engine, OptionCategory maxCategory) { + Map<Instrument, List<PrintableOption>> instrumentsOptions = new HashMap<>(); + List<Instrument> instruments = sortedInstruments(engine); + for (Instrument instrument : instruments) { + List<PrintableOption> options = new ArrayList<>(); + for (OptionDescriptor descriptor : instrument.getOptions()) { + if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { + options.add(asPrintableOption(descriptor)); + } + } + if (!options.isEmpty()) { + instrumentsOptions.put(instrument, options); + } + } + if (!instrumentsOptions.isEmpty()) { + System.out.println(); + System.out.println("Tool options:"); + for (Instrument instrument : instruments) { + List<PrintableOption> options = instrumentsOptions.get(instrument); + if (options != null) { + printOptions(options, " " + instrument.getName() + ":", 4); + } + } + } + } + + private static void printLanguageOptions(Engine engine, OptionCategory maxCategory) { + Map<Language, List<PrintableOption>> languagesOptions = new HashMap<>(); + List<Language> languages = sortedLanguages(engine); + for (Language language : languages) { + List<PrintableOption> options = new ArrayList<>(); + for (OptionDescriptor descriptor : language.getOptions()) { + if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { + options.add(asPrintableOption(descriptor)); + } + } + if (!options.isEmpty()) { + languagesOptions.put(language, options); + } + } + if (!languagesOptions.isEmpty()) { + System.out.println(); + System.out.println("Language Options:"); + for (Language language : languages) { + List<PrintableOption> options = languagesOptions.get(language); + if (options != null) { + printOptions(options, " " + language.getName() + ":", 4); + } + } + } + } + + protected boolean parsePolyglotOption(String defaultOptionPrefix, Map<String, String> options, String arg) { + switch (arg) { + case "--help": + help = true; + return true; + case "--help:debug": + helpDebug = true; + return true; + case "--help:expert": + helpExpert = true; + return true; + case "--help:tools": + helpTools = true; + return true; + case "--help:languages": + helpLanguages = true; + return true; + case "--version": + versionAction = VersionAction.PrintAndExit; + return true; + case "--show-version": + versionAction = VersionAction.PrintAndContinue; + return true; + case "--polyglot": + case "--jvm": + case "--native": + return false; + default: + // getLanguageId() or null? + if (arg.length() <= 2 || !arg.startsWith("--")) { + return false; + } + int eqIdx = arg.indexOf('='); + String key; + String value; + if (eqIdx < 0) { + key = arg.substring(2); + value = null; + } else { + key = arg.substring(2, eqIdx); + value = arg.substring(eqIdx + 1); + } + + if (value == null) { + value = "true"; + } + int index = key.indexOf('.'); + String group = key; + if (index >= 0) { + group = group.substring(0, index); + } + OptionDescriptor descriptor = findPolyglotOptionDescriptor(group, key); + if (descriptor == null) { + if (defaultOptionPrefix != null) { + descriptor = findPolyglotOptionDescriptor(defaultOptionPrefix, defaultOptionPrefix + "." + key); + } + if (descriptor == null) { + return false; + } + } + try { + descriptor.getKey().getType().convert(value); + } catch (IllegalArgumentException e) { + throw abort(String.format("Invalid argument %s specified. %s'", arg, e.getMessage())); + } + options.put(key, value); + return true; + } + } + + private OptionDescriptor findPolyglotOptionDescriptor(String group, String key) { + OptionDescriptors descriptors = null; + switch (group) { + case "compiler": + case "engine": + descriptors = getTempEngine().getOptions(); + break; + default: + Engine engine = getTempEngine(); + if (engine.getLanguages().containsKey(group)) { + descriptors = engine.getLanguage(group).getOptions(); + } else if (engine.getInstruments().containsKey(group)) { + descriptors = engine.getInstrument(group).getOptions(); + } + break; + } + if (descriptors == null) { + return null; + } + return descriptors.get(key); + } + + protected static List<Language> sortedLanguages(Engine engine) { + List<Language> languages = new ArrayList<>(engine.getLanguages().values()); + languages.sort(Comparator.comparing(Language::getId)); + return languages; + } + + protected static List<Instrument> sortedInstruments(Engine engine) { + List<Instrument> instruments = new ArrayList<>(engine.getInstruments().values()); + instruments.sort(Comparator.comparing(Instrument::getId)); + return instruments; + } + + protected static void printOption(OptionCategory maxCategory, OptionDescriptor descriptor) { + if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { + printOption(asPrintableOption(descriptor)); + } + } + + protected static PrintableOption asPrintableOption(OptionDescriptor descriptor) { + StringBuilder key = new StringBuilder("--"); + key.append(descriptor.getName()); + Object defaultValue = descriptor.getKey().getDefaultValue(); + if (defaultValue instanceof Boolean && defaultValue == Boolean.FALSE) { + // nothing to print + } else { + key.append("=<"); + key.append(descriptor.getKey().getType().getName()); + key.append(">"); + } + return new PrintableOption(key.toString(), descriptor.getHelp()); + } + + protected static void printOption(String option, String description) { + printOption(option, description, 2); + } + + protected static void printOption(String option, String description, int indentation) { + StringBuilder indent = new StringBuilder(indentation); + for (int i = 0; i < indentation; i++) { + indent.append(' '); + } + String desc = description != null ? description : ""; + if (option.length() >= 45 && description != null) { + System.out.println(String.format("%s%s%n%s%-45s%s", indent, option, indent, "", desc)); + } else { + System.out.println(String.format("%s%-45s%s", indent, option, desc)); + } + } + + protected static void printOption(PrintableOption option) { + printOption(option, 2); + } + + protected static void printOption(PrintableOption option, int indentation) { + printOption(option.option, option.description, indentation); + } + + private static final class PrintableOption implements Comparable<PrintableOption> { + final String option; + final String description; + + private PrintableOption(String option, String description) { + this.option = option; + this.description = description; + } + + @Override + public int compareTo(PrintableOption o) { + return this.option.compareTo(o.option); + } + } + + private static void printOptions(List<PrintableOption> options, String title, int indentation) { + Collections.sort(options); + System.out.println(title); + for (PrintableOption option : options) { + printOption(option, indentation); + } + } + + protected enum OS { + Darwin, + Linux, + Solaris; + + private static OS findCurrent() { + final String name = System.getProperty("os.name"); + if (name.equals("Linux")) { + return Linux; + } + if (name.equals("SunOS")) { + return Solaris; + } + if (name.equals("Mac OS X") || name.equals("Darwin")) { + return Darwin; + } + throw new IllegalArgumentException("unknown OS: " + name); + } + + private static final OS current = findCurrent(); + + public static OS getCurrent() { + return current; + } + } + + protected static void printLanguages(Engine engine, boolean printWhenEmpty) { + if (engine.getLanguages().isEmpty()) { + if (printWhenEmpty) { + System.out.println(" Installed Languages: none"); + } + } else { + System.out.println(" Installed Languages:"); + List<Language> languages = new ArrayList<>(engine.getLanguages().size()); + int nameLength = 0; + boolean hasHost = false; + for (Language language : engine.getLanguages().values()) { + languages.add(language); + nameLength = max(nameLength, language.getName().length()); + hasHost |= language.isHost(); + } + languages.sort(Comparator.comparing(Language::getId)); + String langFormat = " %-" + nameLength + "s%s version %s%n"; + for (Language language : languages) { + String host; + if (hasHost) { + if (language.isHost()) { + host = " (Host)"; + } else { + host = " "; + } + } else { + host = ""; + } + String version = language.getVersion(); + if (version == null || version.length() == 0) { + version = ""; + } + System.out.printf(langFormat, language.getName().isEmpty() ? "Unnamed" : language.getName(), host, version); + } + } + } + + protected static void printInstruments(Engine engine, boolean printWhenEmpty) { + if (engine.getInstruments().isEmpty()) { + if (printWhenEmpty) { + System.out.println(" Installed Tools: none"); + } + } else { + System.out.println(" Installed Tools:"); + List<Instrument> instruments = sortedInstruments(engine); + int nameLength = 0; + for (Instrument instrument : instruments) { + nameLength = max(nameLength, instrument.getName().length()); + } + String instrumentFormat = " %-" + nameLength + "s version %s%n"; + for (Instrument instrument : instruments) { + String version = instrument.getVersion(); + if (version == null || version.length() == 0) { + version = ""; + } + System.out.printf(instrumentFormat, instrument.getName().isEmpty() ? "Unnamed" : instrument.getName(), version); + } + } + } +} diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java index 7a54ae27c2..55f7b025f7 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java @@ -97,11 +97,11 @@ public final class RCmdOptions { // Whether this option is actually implemented in FastR private final boolean implemented; // The short option name prefixed by {@code -} or {@code null} if no {@code -} form. - private final String shortName; + final String shortName; // The option name prefixed by {@code --} or {@code null} if no {@code --} form. private final String name; // The plain option name as passed to the constructor. - public final String plainName; + final String plainName; // The '=' separated suffix, e.g. {@code --file=FILE}. private final String suffix; // The space separated suffix, e.g. {@code -g TYPE}. @@ -161,6 +161,7 @@ public final class RCmdOptions { } return result; } + } private final EnumMap<RCmdOption, Object> optionValues; @@ -322,7 +323,7 @@ public final class RCmdOptions { } } // adjust for inserted executable name - return new RCmdOptions(options, args, firstNonOptionArgIndex + 1); + return new RCmdOptions(options, args, firstNonOptionArgIndex); } public String[] getArguments() { @@ -337,7 +338,7 @@ public final class RCmdOptions { this.arguments = arguments; } - public void printHelpAndVersion() { + void printHelpAndVersion() { if (getBoolean(HELP)) { throw printHelpAndExit(RCmdOptions.Client.R); } else if (getBoolean(VERSION)) { @@ -345,7 +346,7 @@ public final class RCmdOptions { } } - private static void printHelp(Client client) { + static void printHelp(Client client) { System.out.println(client.usage()); System.out.println("Options:"); for (RCmdOption option : RCmdOption.values()) { @@ -354,16 +355,20 @@ public final class RCmdOptions { System.out.println("\nFILE may contain spaces but not shell metacharacters.\n"); } - public static RuntimeException printHelpAndExit(Client client) { + private static RuntimeException printHelpAndExit(Client client) { printHelp(client); System.exit(0); throw RCommand.fatal("should not reach here"); } - private static RuntimeException printVersionAndExit() { + static void printVersion() { System.out.print("FastR version "); System.out.println(RVersionNumber.FULL); System.out.println(RVersionNumber.LICENSE); + } + + private static RuntimeException printVersionAndExit() { + printVersion(); System.exit(0); throw RCommand.fatal("should not reach here"); } diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java index c4a73a7228..11e4450e7b 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java @@ -28,21 +28,60 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import org.graalvm.options.OptionCategory; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.Source; +import com.oracle.truffle.r.launcher.RCmdOptions.Client; import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption; import jline.console.UserInterruptException; +class RLauncher extends Launcher { + + private final Client client; + + RLauncher(Client client) { + this.client = client; + } + + @Override + protected void printHelp(OptionCategory maxCategory) { + RCmdOptions.printHelp(client); + } + + @Override + protected void printVersion() { + RCmdOptions.printVersion(); + } + + @Override + protected void collectArguments(Set<String> options) { + for (RCmdOption option : RCmdOption.values()) { + if (option.shortName != null) { + options.add(option.shortName); + } + if (option.plainName != null) { + options.add(option.plainName); + } + } + } +} + /* * TODO: + * * - create a replacement for "executor" + * * - fatal needs to be handled more carefully in nested/spawned contexts - * - handle R_DEFAULT_PACKAGES in REnvVars */ /** @@ -77,24 +116,38 @@ public class RCommand { return result; } - public static int doMain(String[] inArgs, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { + public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { StartupTiming.timestamp("Main Entered"); - String[] args = inArgs; + ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args)); if (System.console() != null) { // add "--interactive" to force interactive mode - RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false); - if (!options.getBoolean(RCmdOption.INTERACTIVE)) { - args = new String[inArgs.length + 1]; - args[0] = inArgs[0]; - args[1] = "--interactive"; - System.arraycopy(inArgs, 1, args, 2, inArgs.length - 1); + boolean addArg = true; + for (String arg : args) { + if ("--interactive".equals(arg)) { + addArg = false; + break; + } } + if (addArg) { + argsList.add(1, "--interactive"); + } + } + + RLauncher launcher = new RLauncher(Client.R); + Map<String, String> polyglotOptions = new HashMap<>(); + for (int i = 1; i < argsList.size(); i++) { + String arg = argsList.get(i); + if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) { + argsList.remove(i); + } + } + if (launcher.runPolyglotAction()) { + return 0; } - RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false); - options.printHelpAndVersion(); + RCmdOptions options = RCmdOptions.parseArguments(Client.R, argsList.toArray(new String[argsList.size()]), false); assert env == null : "re-enable setting environments"; ConsoleHandler consoleHandler = createConsoleHandler(options, false, inStream, outStream); - try (Context context = Context.newBuilder().arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) { + try (Context context = Context.newBuilder().options(polyglotOptions).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) { consoleHandler.setContext(context); StartupTiming.timestamp("VM Created"); StartupTiming.printSummary(); @@ -180,7 +233,7 @@ public class RCommand { try { while (true) { // processing inputs boolean doEcho = doEcho(context); - consoleHandler.setPrompt(doEcho ? getPrompt(context) : ""); + consoleHandler.setPrompt(doEcho ? getPrompt(context) : null); try { String input = consoleHandler.readLine(); if (input == null) { @@ -200,7 +253,7 @@ public class RCommand { context.eval(Source.newBuilder("R", sb.toString(), "<REPL>").interactive(true).buildLiteral()); } catch (PolyglotException e) { if (continuePrompt == null) { - continuePrompt = doEcho ? getContinuePrompt(context) : ""; + continuePrompt = doEcho ? getContinuePrompt(context) : null; } if (e.isIncompleteSource()) { // read another line of input diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java index 28c65b70f1..cb3c9782f0 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java @@ -25,9 +25,15 @@ package com.oracle.truffle.r.launcher; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.graalvm.options.OptionCategory; import org.graalvm.polyglot.Context; +import com.oracle.truffle.r.launcher.Launcher.VersionAction; +import com.oracle.truffle.r.launcher.RCmdOptions.Client; import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption; /** @@ -37,17 +43,13 @@ import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption; * */ public class RscriptCommand { + // CheckStyle: stop system..print check - private static void preprocessRScriptOptions(RCmdOptions options) { + private static void preprocessRScriptOptions(RLauncher launcher, RCmdOptions options) { String[] arguments = options.getArguments(); int resultArgsLength = arguments.length; int firstNonOptionArgIndex = options.getFirstNonOptionArgIndex(); - if (options.getBoolean(RCmdOption.HELP)) { - RCmdOptions.printHelpAndExit(RCmdOptions.Client.RSCRIPT); - } else if (options.getBoolean(RCmdOption.VERSION)) { - printVersionAndExit(); - } // Now reformat the args, setting --slave and --no-restore as per the spec ArrayList<String> adjArgs = new ArrayList<>(resultArgsLength + 1); adjArgs.add(arguments[0]); @@ -58,8 +60,9 @@ public class RscriptCommand { // Either -e options are set or first non-option arg is a file if (options.getStringList(RCmdOption.EXPR) == null) { if (firstNonOptionArgIndex == resultArgsLength) { + launcher.setHelpCategory(OptionCategory.USER); // does not return - RCmdOptions.printHelpAndExit(RCmdOptions.Client.RSCRIPT); + launcher.runPolyglotAction(); } else { if (arguments[firstNonOptionArgIndex].startsWith("-")) { throw RCommand.fatal("file name is missing"); @@ -103,23 +106,32 @@ public class RscriptCommand { public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { assert env == null : "re-enble environment variables"; - // Since many of the options are shared parse them from an RSCRIPT perspective. - // Handle --help and --version specially, as they exit. - RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.RSCRIPT, args, false); - preprocessRScriptOptions(options); + + ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args)); + RLauncher launcher = new RLauncher(Client.RSCRIPT) { + @Override + protected void printVersion() { + System.out.print("R scripting front-end version "); + System.out.println(RVersionNumber.FULL); + } + }; + Map<String, String> polyglotOptions = new HashMap<>(); + for (int i = 1; i < argsList.size(); i++) { + String arg = argsList.get(i); + if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) { + argsList.remove(i); + } + } + if (launcher.runPolyglotAction()) { + return 0; + } + RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, argsList.toArray(new String[argsList.size()]), false); + preprocessRScriptOptions(launcher, options); ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, false, inStream, outStream); - try (Context context = Context.newBuilder().arguments("R", - options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) { + try (Context context = Context.newBuilder().options(polyglotOptions).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) { consoleHandler.setContext(context); return RCommand.readEvalPrint(context, consoleHandler); } } - - private static void printVersionAndExit() { - // TODO Not ok in nested context - System.out.print("FastR scripting front-end version "); - System.out.println(RVersionNumber.FULL); - System.exit(0); - } } diff --git a/com.oracle.truffle.r.native/run/Makefile b/com.oracle.truffle.r.native/run/Makefile index 8f04e6ae6a..30e6a1273d 100644 --- a/com.oracle.truffle.r.native/run/Makefile +++ b/com.oracle.truffle.r.native/run/Makefile @@ -76,7 +76,7 @@ $(FASTR_BIN_DIR)/R: Makefile R.sh Rscript.sh Rscript_exec.sh Rclasspath.sh cp Rclasspath.sh $(FASTR_BIN_DIR)/execRextras/Rclasspath chmod +x $(FASTR_BIN_DIR)/exec/R $(FASTR_BIN_DIR)/execRextras/Rscript $(FASTR_BIN_DIR)/Rscript $(FASTR_BIN_DIR)/execRextras/Rclasspath # update R_HOME_DIR to FastR - sed -e 's!^\(R_HOME_DIR=\)\(.*\)!\1"$(FASTR_R_HOME)"!' < $(R_SCRIPT) > $(FASTR_BIN_DIR)/R + (sed -e 's!^\(R_HOME_DIR=\)\(.*\)!\1"$(FASTR_R_HOME)"!' | sed -e 's/ -h.--help./ --r-help\)/') < $(R_SCRIPT) > $(FASTR_BIN_DIR)/R chmod +x $(FASTR_BIN_DIR)/R touch $(FASTR_ETC_DIR)/ldpaths sed -e "s|\(R_LIBS_USER=.*-'\)\(.*\)'}|\1$(FASTR_R_HOME)/library'}|" < $(GNUR_HOME)/etc/Renviron > $(FASTR_ETC_DIR)/Renviron -- GitLab