diff --git a/src/dotnet/CommandLine/CommandLineApplication.cs b/src/dotnet/CommandLine/CommandLineApplication.cs index 892631207..68a750daf 100644 --- a/src/dotnet/CommandLine/CommandLineApplication.cs +++ b/src/dotnet/CommandLine/CommandLineApplication.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -42,6 +43,7 @@ namespace Microsoft.DotNet.Cli.CommandLine public Func LongVersionGetter { get; set; } public Func ShortVersionGetter { get; set; } public List Commands { get; private set; } + public bool HandleResponseFiles { get; set; } public CommandLineApplication Command(string name, Action configuration, bool throwOnUnexpectedArg = true) @@ -102,6 +104,11 @@ namespace Microsoft.DotNet.Cli.CommandLine CommandOption option = null; IEnumerator arguments = null; + if (HandleResponseFiles) + { + args = ExpandResponseFiles(args).ToArray(); + } + for (var index = 0; index < args.Length; index++) { var arg = args[index]; @@ -151,7 +158,7 @@ namespace Microsoft.DotNet.Cli.CommandLine } option = null; } - else if (option.OptionType == CommandOptionType.NoValue) + else if (option.OptionType == CommandOptionType.NoValue || option.OptionType == CommandOptionType.BoolValue) { // No value is needed for this option option.TryParse(null); @@ -196,7 +203,7 @@ namespace Microsoft.DotNet.Cli.CommandLine } option = null; } - else if (option.OptionType == CommandOptionType.NoValue) + else if (option.OptionType == CommandOptionType.NoValue || option.OptionType == CommandOptionType.BoolValue) { // No value is needed for this option option.TryParse(null); @@ -479,6 +486,50 @@ namespace Microsoft.DotNet.Cli.CommandLine } } + private IEnumerable ExpandResponseFiles(IEnumerable args) + { + foreach (var arg in args) + { + if (!arg.StartsWith("@", StringComparison.Ordinal)) + { + yield return arg; + } + else + { + var fileName = arg.Substring(1); + + var responseFileArguments = ParseResponseFile(fileName); + + // ParseResponseFile can suppress expanding this response file by + // returning null. In that case, we'll treat the response + // file token as a regular argument. + + if (responseFileArguments == null) + { + yield return arg; + } + else + { + foreach (var responseFileArgument in responseFileArguments) + yield return responseFileArgument.Trim(); + } + } + } + } + + private IEnumerable ParseResponseFile(string fileName) + { + if (!HandleResponseFiles) + return null; + + if (!File.Exists(fileName)) + { + throw new InvalidOperationException($"Response file '{fileName}' doesn't exist."); + } + + return File.ReadLines(fileName); + } + private class CommandArgumentEnumerator : IEnumerator { private readonly IEnumerator _enumerator; diff --git a/src/dotnet/CommandLine/CommandOption.cs b/src/dotnet/CommandLine/CommandOption.cs index 7379ec5a2..793ea8da0 100644 --- a/src/dotnet/CommandLine/CommandOption.cs +++ b/src/dotnet/CommandLine/CommandOption.cs @@ -39,8 +39,12 @@ namespace Microsoft.DotNet.Cli.CommandLine { ValueName = part.Substring(1, part.Length - 2); } - else + else if (optionType == CommandOptionType.MultipleValue && part.StartsWith("<") && part.EndsWith(">...")) { + ValueName = part.Substring(1, part.Length - 5); + } + else + { throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template)); } } @@ -58,6 +62,7 @@ namespace Microsoft.DotNet.Cli.CommandLine public string ValueName { get; set; } public string Description { get; set; } public List Values { get; private set; } + public bool? BoolValue { get; private set; } public CommandOptionType OptionType { get; private set; } public bool TryParse(string value) @@ -74,6 +79,30 @@ namespace Microsoft.DotNet.Cli.CommandLine } Values.Add(value); break; + case CommandOptionType.BoolValue: + if (Values.Any()) + { + return false; + } + + if (value == null) + { + // add null to indicate that the option was present, but had no value + Values.Add(null); + BoolValue = true; + } + else + { + bool boolValue; + if (!bool.TryParse(value, out boolValue)) + { + return false; + } + + Values.Add(value); + BoolValue = boolValue; + } + break; case CommandOptionType.NoValue: if (value != null) { diff --git a/src/dotnet/CommandLine/CommandOptionType.cs b/src/dotnet/CommandLine/CommandOptionType.cs index ccf96e68b..6cee7406b 100644 --- a/src/dotnet/CommandLine/CommandOptionType.cs +++ b/src/dotnet/CommandLine/CommandOptionType.cs @@ -8,6 +8,7 @@ namespace Microsoft.DotNet.Cli.CommandLine { MultipleValue, SingleValue, + BoolValue, NoValue } }