/// <summary> /// Parse the command line using the options defined in this argument parser. /// </summary> /// <param name="args">The command line.</param> /// <returns>The parsed arguments.</returns> public List <CommandLineArgument> ParseArguments(string[] args) { List <CommandLineArgument> result = new List <CommandLineArgument>(); int position = 0; // For positional arguments. CommandLineArgument current = null; for (int idx = 0; idx < args.Length; idx++) { string arg = args[idx]; if (arg.StartsWith("-")) { if (arg.StartsWith("--")) { var name = arg.Substring(2); current = null; this.Arguments.TryGetValue(name, out current); } else if (arg.StartsWith("-")) { current = null; var name = arg.Substring(1); // Note that "/" is not supported as an argument delimiter because it conflicts with unix file paths. foreach (var s in this.Arguments.Values) { if (s.ShortName == name) { current = s; break; } } if (current == null) { // See if there's a matching long name with no short name defined. foreach (var s in this.Arguments.Values) { if (s.LongName == name) { current = s; break; } } } } if (current == null) { throw new Exception(string.Format("Unexpected argument: '{0}'", arg)); } current = current.Clone(); result.Add(current); if (current.PrintHelp) { this.PrintHelp(Console.Out); Environment.Exit(1); } } else if (current != null) { // The value for the current switch argument. current.AddParsedValue(arg); } else { // Positional arguments. do { if (position < this.PositionalNames.Count) { var name = this.PositionalNames[position++]; current = this.Arguments[name]; } else { throw new Exception(string.Format("Unexpected positional argument: '{0}'", arg)); } }while (!IsRequired(current, result)); // Positional arguments have no name so the arg is the value. var temp = current.Clone(); temp.Value = current.ParseValue(arg); result.Add(temp); current = null; // This argument is done, cannot have any more values. } } foreach (var arg in this.Arguments.Values) { if (IsRequired(arg, result) && !(from r in result where r.LongName == arg.LongName select r).Any()) { if (arg.IsPositional) { throw new Exception(string.Format("Missing required argument: '{0}'", arg.LongName)); } else { throw new Exception(string.Format("Missing required argument: '--{0}'", arg.LongName)); } } } foreach (var arg in result) { if (!arg.IsPositional && arg.Value == null && arg.DataType != typeof(bool) && !arg.AllowedValues.Contains(string.Empty)) { throw new Exception(string.Format("Missing value for argument: '--{0}'", arg.LongName)); } } return(result); }
/// <summary> /// Updates the configuration with the specified parsed argument. /// </summary> private static void UpdateConfigurationWithParsedArgument(Configuration configuration, RewritingOptions options, CommandLineArgument option) { switch (option.LongName) { case "command": configuration.ToolCommand = (string)option.Value; break; case "outdir": configuration.OutputFilePath = (string)option.Value; break; case "debug": configuration.IsDebugVerbosityEnabled = true; Debug.IsEnabled = true; break; case "verbosity": configuration.IsVerbose = true; string verbosity = (string)option.Value; switch (verbosity) { case "quiet": configuration.IsVerbose = false; break; case "detailed": configuration.LogLevel = options.LogLevel = LogSeverity.Informational; break; case "normal": configuration.LogLevel = options.LogLevel = LogSeverity.Warning; break; case "minimal": configuration.LogLevel = options.LogLevel = LogSeverity.Error; break; default: Error.ReportAndExit($"Please give a valid value for 'verbosity' must be one of 'errors', 'warnings', or 'info', but found {verbosity}."); break; } break; case "path": if (configuration.ToolCommand is "test" || configuration.ToolCommand is "replay") { // In the case of 'coyote test' or 'replay', the path is the assembly to be tested. configuration.AssemblyToBeAnalyzed = (string)option.Value; } else if (configuration.ToolCommand is "rewrite") { // In the case of 'coyote rewrite', the path is the JSON configuration file // with the binary rewriting options. string filename = (string)option.Value; if (Directory.Exists(filename)) { // then we want to rewrite a whole folder full of dll's. configuration.RewritingOptionsPath = filename; } else { string extension = Path.GetExtension(filename); if (string.Compare(extension, ".json", StringComparison.OrdinalIgnoreCase) is 0) { configuration.RewritingOptionsPath = filename; } else if (string.Compare(extension, ".dll", StringComparison.OrdinalIgnoreCase) is 0 || string.Compare(extension, ".exe", StringComparison.OrdinalIgnoreCase) is 0) { configuration.AssemblyToBeAnalyzed = filename; } else { Error.ReportAndExit("Please give a valid .dll or JSON configuration file for binary rewriting."); } }
/// <summary> /// Updates the configuration with the specified parsed argument. /// </summary> private static void UpdateConfigurationWithParsedArgument(Configuration configuration, CommandLineArgument option) { switch (option.LongName) { case "command": configuration.ToolCommand = (string)option.Value; break; case "outdir": configuration.OutputFilePath = (string)option.Value; break; case "verbose": configuration.IsVerbose = true; break; case "debug": configuration.EnableDebugging = true; Debug.IsEnabled = true; break; case "timeout": configuration.Timeout = (int)(uint)option.Value; break; case "path": if (configuration.ToolCommand is "test" || configuration.ToolCommand is "replay") { // In the case of 'coyote test' or 'replay', the path is the assembly to be tested. configuration.AssemblyToBeAnalyzed = (string)option.Value; } else if (configuration.ToolCommand is "rewrite") { // In the case of 'coyote rewrite', the path is the JSON configuration file // with the binary rewriting options. string filename = (string)option.Value; string extension = Path.GetExtension(filename); if (string.Compare(extension, ".json", StringComparison.OrdinalIgnoreCase) == 0) { configuration.RewritingConfigurationFile = filename; } else if (string.Compare(extension, ".dll", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(extension, ".exe", StringComparison.OrdinalIgnoreCase) == 0) { configuration.AssemblyToBeAnalyzed = filename; } else { Error.ReportAndExit("Please give a valid .dll or JSON configuration file for binary rewriting."); } } break;
/// <summary> /// Updates the configuration with the specified parsed argument. /// </summary> private static void UpdateConfigurationWithParsedArgument(Configuration configuration, CommandLineArgument option) { switch (option.LongName) { case "command": configuration.ToolCommand = (string)option.Value; break; case "outdir": configuration.OutputFilePath = (string)option.Value; break; case "verbose": configuration.IsVerbose = true; break; case "debug": configuration.EnableDebugging = true; Debug.IsEnabled = true; break; case "timeout": configuration.Timeout = (int)(uint)option.Value; break; case "path": configuration.AssemblyToBeAnalyzed = (string)option.Value; break; case "method": configuration.TestMethodName = (string)option.Value; break; case "seed": configuration.RandomGeneratorSeed = (uint)option.Value; break; case "sch-random": case "sch-dfs": case "sch-portfolio": configuration.SchedulingStrategy = option.LongName.Substring(4); break; case "sch-probabilistic": case "sch-pct": case "sch-fairpct": configuration.SchedulingStrategy = option.LongName.Substring(4); configuration.StrategyBound = (int)(uint)option.Value; break; case "schedule": { string filename = (string)option.Value; string extension = System.IO.Path.GetExtension(filename); if (!extension.Equals(".schedule")) { Error.ReportAndExit("Please give a valid schedule file " + "'--replay x', where 'x' has extension '.schedule'."); } configuration.ScheduleFile = filename; } break; case "break": configuration.AttachDebugger = true; break; case "iterations": configuration.TestingIterations = (int)(uint)option.Value; break; case "parallel": configuration.ParallelBugFindingTasks = (uint)option.Value; break; case "parallel-debug": configuration.ParallelDebug = true; break; case "wait-for-testing-processes": configuration.WaitForTestingProcesses = true; break; case "testing-scheduler-ipaddress": { var ipAddress = (string)option.Value; int port = 0; if (ipAddress.Contains(":")) { string[] parts = ipAddress.Split(':'); if (parts.Length != 2 || !int.TryParse(parts[1], out port)) { Error.ReportAndExit("Please give a valid port number for --testing-scheduler-ipaddress option"); } ipAddress = parts[0]; } if (!IPAddress.TryParse(ipAddress, out _)) { Error.ReportAndExit("Please give a valid ip address for --testing-scheduler-ipaddress option"); } configuration.TestingSchedulerIpAddress = ipAddress + ":" + port; } break; case "run-as-parallel-testing-task": configuration.RunAsParallelBugFindingTask = true; break; case "testing-scheduler-endpoint": configuration.TestingSchedulerEndPoint = (string)option.Value; break; case "testing-process-id": configuration.TestingProcessId = (uint)option.Value; break; case "graph": configuration.IsDgmlGraphEnabled = true; configuration.IsDgmlBugGraph = false; break; case "graph-bug": configuration.IsDgmlGraphEnabled = true; configuration.IsDgmlBugGraph = true; break; case "xml-trace": configuration.IsXmlLogEnabled = true; break; case "actor-runtime-log": configuration.CustomActorRuntimeLogType = (string)option.Value; break; case "explore": configuration.PerformFullExploration = true; break; case "coverage": if (option.Value == null) { configuration.ReportCodeCoverage = true; configuration.ReportActivityCoverage = true; } else { foreach (var item in (string[])option.Value) { switch (item) { case "code": configuration.ReportCodeCoverage = true; break; case "activity": configuration.ReportActivityCoverage = true; break; case "activity-debug": configuration.ReportActivityCoverage = true; configuration.DebugActivityCoverage = true; break; default: break; } } } break; case "instrument": case "instrument-list": configuration.AdditionalCodeCoverageAssemblies[(string)option.Value] = false; break; case "timeout-delay": configuration.TimeoutDelay = (uint)option.Value; break; case "max-steps": { uint[] values = (uint[])option.Value; if (values.Length > 2) { Error.ReportAndExit("Invalid number of options supplied via '--max-steps'."); } uint i = values[0]; uint j; if (values.Length == 2) { j = values[1]; configuration.UserExplicitlySetMaxFairSchedulingSteps = true; } else { j = 10 * i; } configuration.MaxUnfairSchedulingSteps = (int)i; configuration.MaxFairSchedulingSteps = (int)j; } break; case "fail-on-maxsteps": configuration.ConsiderDepthBoundHitAsBug = true; break; case "prefix": configuration.SafetyPrefixBound = (int)option.Value; break; case "liveness-temperature-threshold": configuration.LivenessTemperatureThreshold = (int)(uint)option.Value; break; default: throw new Exception(string.Format("Unhandled parsed argument: '{0}'", option.LongName)); } }