From 83f3a3ec86841c68f5295d406a85581c68a69cca Mon Sep 17 00:00:00 2001 From: William Li Date: Wed, 12 Apr 2017 16:03:45 -0700 Subject: [PATCH 1/2] Fix dotnet run double dash passing arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When run “dotnet run -- foo”, foo should be the argument passed to the subject app. After replacing the original parser, dotnet-run did not utilize the “unparsedtoken” of the parsed result. To append unparsedtoken to RunCommand’s argument is not straight forward. RunCommand has an “immutable constructor”, which is a good thing, so I made update RunCommand’s argument following the immutable pattern -- create a new object with the original field but only change the arguments. I also made these filed private set. --- .../TestProjects/MSBuildTestApp/Program.cs | 4 ++ .../commands/dotnet-complete/ParseCommand.cs | 7 +++- src/dotnet/commands/dotnet-run/Program.cs | 23 +++++++---- src/dotnet/commands/dotnet-run/RunCommand.cs | 38 ++++++++++++++++--- .../commands/dotnet-run/RunCommandParser.cs | 16 ++++---- .../GivenDotnetRunRunsCsProj.cs | 19 ++++++++++ .../ParserTests/RunParserTests.cs | 29 ++++++++++++++ 7 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 test/dotnet.Tests/ParserTests/RunParserTests.cs diff --git a/TestAssets/TestProjects/MSBuildTestApp/Program.cs b/TestAssets/TestProjects/MSBuildTestApp/Program.cs index 20b3f028d..353ad5fbd 100644 --- a/TestAssets/TestProjects/MSBuildTestApp/Program.cs +++ b/TestAssets/TestProjects/MSBuildTestApp/Program.cs @@ -9,6 +9,10 @@ namespace MSBuildTestApp { public static void Main(string[] args) { + if (args.Length > 0) + { + Console.WriteLine("echo args:"+ String.Join(";", args)); + } Console.WriteLine("Hello World!"); } } diff --git a/src/dotnet/commands/dotnet-complete/ParseCommand.cs b/src/dotnet/commands/dotnet-complete/ParseCommand.cs index 4a09e0468..dc86dab3d 100644 --- a/src/dotnet/commands/dotnet-complete/ParseCommand.cs +++ b/src/dotnet/commands/dotnet-complete/ParseCommand.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Utils; @@ -24,6 +24,11 @@ namespace Microsoft.DotNet.Cli Console.WriteLine(result.Diagram()); + if (result.UnparsedTokens.Any()) { + Console.WriteLine("Unparsed Tokens: "); + Console.WriteLine(string.Join(" ", (result.UnparsedTokens))); + } + var optionValuesToBeForwarded = result.AppliedCommand() .OptionValuesToBeForwarded(); if (optionValuesToBeForwarded.Any()) diff --git a/src/dotnet/commands/dotnet-run/Program.cs b/src/dotnet/commands/dotnet-run/Program.cs index 6bfaffefa..75c37cdca 100644 --- a/src/dotnet/commands/dotnet-run/Program.cs +++ b/src/dotnet/commands/dotnet-run/Program.cs @@ -1,8 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using Microsoft.DotNet.Cli.CommandLine; +using System.Collections.Generic; +using System.Linq; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli; using Parser = Microsoft.DotNet.Cli.Parser; @@ -13,13 +13,22 @@ namespace Microsoft.DotNet.Tools.Run { public static RunCommand FromArgs(string[] args, string msbuildPath = null) { - var parser = Parser.Instance; - - var result = parser.ParseFrom("dotnet run", args); + var result = Parser.Instance.ParseFrom("dotnet run", args); result.ShowHelpOrErrorIfAppropriate(); - return result["dotnet"]["run"].Value(); + var runCommand = result["dotnet"]["run"].Value(); + return IncludingArgumentsAfterDoubleDash(runCommand, result.UnparsedTokens); + } + + private static RunCommand IncludingArgumentsAfterDoubleDash( + RunCommand runCommand, + IEnumerable unparsedTokens) + { + return runCommand.MakeNewWithReplaced( + args: runCommand.Args + .Concat(unparsedTokens) + .ToList()); } public static int Run(string[] args) @@ -27,7 +36,7 @@ namespace Microsoft.DotNet.Tools.Run DebugHelper.HandleDebugSwitch(ref args); RunCommand cmd; - + try { cmd = FromArgs(args); diff --git a/src/dotnet/commands/dotnet-run/RunCommand.cs b/src/dotnet/commands/dotnet-run/RunCommand.cs index 240b3971f..5a60765ee 100644 --- a/src/dotnet/commands/dotnet-run/RunCommand.cs +++ b/src/dotnet/commands/dotnet-run/RunCommand.cs @@ -13,11 +13,11 @@ namespace Microsoft.DotNet.Tools.Run { public partial class RunCommand { - public string Configuration { get; set; } - public string Framework { get; set; } - public bool NoBuild { get; set; } - public string Project { get; set; } - public IReadOnlyCollection Args { get; set; } + public string Configuration { get; private set; } + public string Framework { get; private set; } + public bool NoBuild { get; private set; } + public string Project { get; private set; } + public IReadOnlyCollection Args { get; private set; } private List _args; private bool ShouldBuild => !NoBuild; @@ -38,6 +38,34 @@ namespace Microsoft.DotNet.Tools.Run .ExitCode; } + public RunCommand(string configuration, + string framework, + bool noBuild, + string project, + IReadOnlyCollection args) + { + Configuration = configuration; + Framework = framework; + NoBuild = noBuild; + Project = project; + Args = args; + } + + public RunCommand MakeNewWithReplaced(string configuration = null, + string framework = null, + bool? noBuild = null, + string project = null, + IReadOnlyCollection args = null) + { + return new RunCommand( + configuration ?? this.Configuration, + framework ?? this.Framework, + noBuild ?? this.NoBuild, + project ?? this.Project, + args ?? this.Args + ); + } + private void EnsureProjectIsBuilt() { List buildArgs = new List(); diff --git a/src/dotnet/commands/dotnet-run/RunCommandParser.cs b/src/dotnet/commands/dotnet-run/RunCommandParser.cs index db6c64a70..54f439d79 100644 --- a/src/dotnet/commands/dotnet-run/RunCommandParser.cs +++ b/src/dotnet/commands/dotnet-run/RunCommandParser.cs @@ -15,14 +15,14 @@ namespace Microsoft.DotNet.Cli LocalizableStrings.AppFullName, treatUnmatchedTokensAsErrors: false, arguments: Accept.ZeroOrMoreArguments() - .MaterializeAs(o => new RunCommand - { - Configuration = o.SingleArgumentOrDefault("--configuration"), - Framework = o.SingleArgumentOrDefault("--framework"), - NoBuild = o.HasOption("--no-build"), - Project = o.SingleArgumentOrDefault("--project"), - Args = o.Arguments - }), + .MaterializeAs(o => new RunCommand + ( + configuration: o.SingleArgumentOrDefault("--configuration"), + framework: o.SingleArgumentOrDefault("--framework"), + noBuild: o.HasOption("--no-build"), + project: o.SingleArgumentOrDefault("--project"), + args: o.Arguments + )), options: new[] { CommonOptions.HelpOption(), diff --git a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs b/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs index 86d9fb0e5..8ce1730be 100644 --- a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs +++ b/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs @@ -181,5 +181,24 @@ namespace Microsoft.DotNet.Cli.Run.Tests .Should().Fail() .And.HaveStdErrContaining("--framework"); } + + [Fact] + public void ItCanPassArgumentsToSubjectAppByDoubleDash() + { + const string testAppName = "MSBuildTestApp"; + var testInstance = TestAssets.Get(testAppName) + .CreateInstance() + .WithSourceFiles() + .WithRestoreFiles(); + + var testProjectDirectory = testInstance.Root.FullName; + + new RunCommand() + .WithWorkingDirectory(testProjectDirectory) + .ExecuteWithCapturedOutput("-- foo bar baz") + .Should() + .Pass() + .And.HaveStdOutContaining("echo args:foo;bar;baz"); + } } } diff --git a/test/dotnet.Tests/ParserTests/RunParserTests.cs b/test/dotnet.Tests/ParserTests/RunParserTests.cs new file mode 100644 index 000000000..6fdee951b --- /dev/null +++ b/test/dotnet.Tests/ParserTests/RunParserTests.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Linq; +using FluentAssertions; +using Microsoft.DotNet.Tools.Run; +using Xunit; +using Xunit.Abstractions; +using System; + +namespace Microsoft.DotNet.Tests.ParserTests +{ + public class RunParserTests + { + public RunParserTests(ITestOutputHelper output) + { + this.output = output; + } + + private readonly ITestOutputHelper output; + + [Fact] + public void RunParserCanGetArguementFromDoubleDash() + { + var runCommand = RunCommand.FromArgs(new[]{ "--", "foo" }); + runCommand.Args.Single().Should().Be("foo"); + } + } +} From def43227833a94a0bab661086773091de8726107 Mon Sep 17 00:00:00 2001 From: William Li Date: Fri, 14 Apr 2017 10:27:24 -0700 Subject: [PATCH 2/2] Fix format --- TestAssets/TestProjects/MSBuildTestApp/Program.cs | 4 ++-- src/dotnet/commands/dotnet-complete/ParseCommand.cs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/TestAssets/TestProjects/MSBuildTestApp/Program.cs b/TestAssets/TestProjects/MSBuildTestApp/Program.cs index 353ad5fbd..e50829e81 100644 --- a/TestAssets/TestProjects/MSBuildTestApp/Program.cs +++ b/TestAssets/TestProjects/MSBuildTestApp/Program.cs @@ -9,9 +9,9 @@ namespace MSBuildTestApp { public static void Main(string[] args) { - if (args.Length > 0) + if (args.Length > 0) { - Console.WriteLine("echo args:"+ String.Join(";", args)); + Console.WriteLine("echo args:" + string.Join(";", args)); } Console.WriteLine("Hello World!"); } diff --git a/src/dotnet/commands/dotnet-complete/ParseCommand.cs b/src/dotnet/commands/dotnet-complete/ParseCommand.cs index dc86dab3d..dccba0271 100644 --- a/src/dotnet/commands/dotnet-complete/ParseCommand.cs +++ b/src/dotnet/commands/dotnet-complete/ParseCommand.cs @@ -24,9 +24,10 @@ namespace Microsoft.DotNet.Cli Console.WriteLine(result.Diagram()); - if (result.UnparsedTokens.Any()) { + if (result.UnparsedTokens.Any()) + { Console.WriteLine("Unparsed Tokens: "); - Console.WriteLine(string.Join(" ", (result.UnparsedTokens))); + Console.WriteLine(string.Join(" ", result.UnparsedTokens)); } var optionValuesToBeForwarded = result.AppliedCommand()