From 87bb2f581459a1118be7c049981d111b45fd7a8a Mon Sep 17 00:00:00 2001 From: Aaron Bockover Date: Wed, 22 Mar 2017 10:30:36 -0400 Subject: [PATCH] csharp: support command line arguments for scripts This adds a command line argument preprocessor that splits command line arguments into two groups, delimited by the "standard" `--` "stop processing" argument, or by the `-s` argument which can be used with Unix shebang syntax (where the path of the file being executed will be appended to the command line specified on the shebang line). - anything before `--` or `-s` will be passed to mcs/csharp as usual - anything after `--` or `-s` will _not_ be processed by mcs/csharp, and will be made available in an `Args` builtin, like `csi` This _finally_ (after almost 10 years) allows `csharp` to be used for proper script authoring! Document the new arguments in the man page. Also remove references to 'gsharp' from the man page. --- man/csharp.1 | 75 ++++++++++++++++++++++++++++++---------- mcs/tools/csharp/repl.cs | 61 +++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 19 deletions(-) diff --git a/man/csharp.1 b/man/csharp.1 index a0f9e6f4f6a..430a1608968 100644 --- a/man/csharp.1 +++ b/man/csharp.1 @@ -2,31 +2,22 @@ .if t .sp .5v .if n .sp .. -.TH csharp 1 "4 September 2008" +.TH csharp 1 "22 March 2017" .SH NAME -csharp, gsharp \- Interactive C# Shell +csharp \- Interactive C# Shell and Scripting .SH SYNOPSIS .B csharp [--attach PID] [-e EXPRESSION] [file1 [file2]] -[options] -.P -.B gsharp [file1 [file2]] +[compiler-options] [--|-s script-options] .SH DESCRIPTION The .I csharp -is an interactive C# shell that allows the user to enter and evaluate -C# statements and expressions from the command line. The regular +command is an interactive C# shell and scripting host that allows +the user to enter and evaluate C# statements and expressions from +the command line or execute C# scripts. +The regular .I mcs command line options can be used in this version of the compiler. .PP -The -.I gsharp -command is a GUI version of the C# interpreter that uses Gtk# and -provides an area to attach widgets as well. This version can be -attached to other Gtk# applications in a safe way as it injects itself -into the main loop of a Gtk# application, avoiding any problems -arising from the multi-threaded nature of injecting itself into a -target process. -.PP Files specified in the command line will be loaded and executed as scripts. .PP @@ -35,9 +26,31 @@ Starting with Mono 2.10, the command can be used as an interpreter executed by executables flagged with the Unix execute attribute. To do this, make the first line of your C# source code look like this: +.PP .nf -"#!/usr/bin/csharp" -Console.WriteLine ("Hello, World"); + #!/usr/bin/csharp + Console.WriteLine ("Hello, World"); +.fi +.PP +Starting with Mono 5.0, command line arguments may now be passed +to the +.I csharp +command by specifying either the +.I -s +or +.I -- +(script) options. +.PP +The +.I -s +option is ideal for interpreting executable scripts that utilize +shebang syntax (introduced in Mono 2.10). This allows command line +arguments to be passed to and consumed cleanly by the script: +.PP +.nf + #!/usr/bin/csharp -s + foreach (var arg in Args) + Console.WriteLine ($"script argument: {arg}"); .fi .SH OPTIONS The commands accept all of the commands that are available to the @@ -45,6 +58,23 @@ The commands accept all of the commands that are available to the command, so you can reference assemblies, specify paths, language level and so on from the command line. In addition, the following command line options are supported: +.TP +.I "\-s" SCRIPT_FILE +This option is ideal for authoring executable scripts that utilize +the Unix shebang feature. Unix will implicitly append as an argument +the path of the script to execute. When the executable is invoked, +any arguments then passed to it will be available in the +.I Args +global. Example: +.I "#!/usr/bin/env csharp -s" +.TP +.I "\-\-" +Any arguments that follow will not be passed to the compiler driver, +and instead will be made available in the +.I Args +global. Example: +.I csharp -- a b c +will result in Args = { "a", "b", "c" } in the interactive shell. .TP .I "\-\-attach" This is an advanced option and should only be used if you have a deep @@ -290,6 +320,15 @@ statements and expressions are executed are static, they can be invoked directly from the shell. These are the available properties and methods: .TP +.I Args +An easy to consume array of any arguments specified after either +.I -s +or +.I -- +on the command line. Ideal for self-executing scripts utilizing the +.I -s +option. +.TP .I void LoadAssembly(string assembly) Loads the given assembly. This is equivalent to passing the compiler the -r: flag with the specified string. diff --git a/mcs/tools/csharp/repl.cs b/mcs/tools/csharp/repl.cs index 567c766e6cb..87714c727ec 100644 --- a/mcs/tools/csharp/repl.cs +++ b/mcs/tools/csharp/repl.cs @@ -38,9 +38,15 @@ namespace Mono { static string target_host; static int target_port; static string agent; + static string [] script_args; + + public static string [] ScriptArgs => script_args; static int Main (string [] args) { + if (!SplitDriverAndScriptArguments (ref args, out script_args)) + return 1; + var cmd = new CommandLineParser (Console.Out); cmd.UnknownOptionHandler += HandleExtraArguments; @@ -90,6 +96,55 @@ namespace Mono { return shell.Run (startup_files); } + static bool SplitDriverAndScriptArguments (ref string [] driver_args, out string [] script_args) + { + // split command line arguments into two groups: + // - anything before '--' or '-s' goes to the mcs driver, which may + // call back into the csharp driver for further processing + // - anything after '--' or '-s' is made available to the REPL/script + // via the 'Args' global, similar to csi. + // - if '-s' is used, the argument immediately following it will + // also be processed by the mcs driver (e.g. a source file) + + int driver_args_count = 0; + int script_args_offset = 0; + string script_file = null; + + while (driver_args_count < driver_args.Length && script_args_offset == 0) { + switch (driver_args [driver_args_count]) { + case "--": + script_args_offset = driver_args_count + 1; + break; + case "-s": + if (driver_args_count + 1 >= driver_args.Length) { + script_args = null; + Console.Error.WriteLine ("usage is: -s SCRIPT_FILE"); + return false; + } + driver_args_count++; + script_file = driver_args [driver_args_count]; + script_args_offset = driver_args_count + 1; + break; + default: + driver_args_count++; + break; + } + } + + if (script_args_offset > 0) { + int script_args_count = driver_args.Length - script_args_offset; + script_args = new string [script_args_count]; + Array.Copy (driver_args, script_args_offset, script_args, 0, script_args_count); + } else + script_args = Array.Empty (); + + Array.Resize (ref driver_args, driver_args_count); + if (script_file != null) + driver_args [driver_args_count - 1] = script_file; + + return true; + } + static int HandleExtraArguments (string [] args, int pos) { switch (args [pos]) { @@ -163,9 +218,13 @@ namespace Mono { public static new string help { get { return InteractiveBase.help + - " TabAtStartCompletes - Whether tab will complete even on empty lines\n"; + " TabAtStartCompletes - Whether tab will complete even on empty lines\n" + + " Args - Any command line arguments passed to csharp\n" + + " after the '--' (stop processing) argument"; } } + + public static string [] Args => Driver.ScriptArgs; } public class CSharpShell { -- 2.25.1