Add support for '--' argument separator
Also refactors command line parsing into a separate class.
This commit is contained in:
parent
11bbd6df8e
commit
f90594a647
|
|
@ -29,4 +29,5 @@ project.lock.json
|
|||
.build/
|
||||
/.vs/
|
||||
testWorkDir/
|
||||
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
|
|
@ -33,6 +33,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Secret
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.SecretManager.Tools.Tests", "test\Microsoft.Extensions.SecretManager.Tools.Tests\Microsoft.Extensions.SecretManager.Tools.Tests.xproj", "{7B331122-83B1-4F08-A119-DC846959844C}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Watcher.Tools.Tests", "test\Microsoft.DotNet.Watcher.Tools.Tests\Microsoft.DotNet.Watcher.Tools.Tests.xproj", "{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Caching.SqlConfig.Tools", "src\Microsoft.Extensions.Caching.SqlConfig.Tools\Microsoft.Extensions.Caching.SqlConfig.Tools.xproj", "{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}"
|
||||
EndProject
|
||||
Global
|
||||
|
|
@ -81,6 +83,10 @@ Global
|
|||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -97,5 +103,6 @@ Global
|
|||
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E} = {66517987-2A5A-4330-B130-207039378FD4}
|
||||
{7B331122-83B1-4F08-A119-DC846959844C} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
|
||||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9} = {66517987-2A5A-4330-B130-207039378FD4}
|
||||
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Watcher.Core.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ namespace Microsoft.DotNet.Watcher.Core
|
|||
_logger = _loggerFactory.CreateLogger(nameof(DotNetWatcher));
|
||||
}
|
||||
|
||||
public async Task WatchAsync(string projectFile, string[] dotnetArguments, CancellationToken cancellationToken)
|
||||
public async Task WatchAsync(string projectFile, IEnumerable<string> dotnetArguments, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(projectFile))
|
||||
{
|
||||
|
|
@ -48,24 +50,7 @@ namespace Microsoft.DotNet.Watcher.Core
|
|||
throw new ArgumentNullException(nameof(cancellationToken));
|
||||
}
|
||||
|
||||
// If any argument has spaces then quote it because we're going to convert everything
|
||||
// to string
|
||||
for (var i = 0; i < dotnetArguments.Length; i++)
|
||||
{
|
||||
var arg = dotnetArguments[i];
|
||||
foreach (char c in arg)
|
||||
{
|
||||
if (c == ' ' ||
|
||||
c == '\t')
|
||||
{
|
||||
arg = $"\"{arg}\"";
|
||||
break;
|
||||
}
|
||||
}
|
||||
dotnetArguments[i] = arg;
|
||||
}
|
||||
|
||||
var dotnetArgumentsAsString = string.Join(" ", dotnetArguments);
|
||||
var dotnetArgumentsAsString = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(dotnetArguments);
|
||||
|
||||
var workingDir = Path.GetDirectoryName(projectFile);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
|
||||
"Microsoft.DotNet.Cli.Utils": "1.0.0-*",
|
||||
"Microsoft.Extensions.FileProviders.Abstractions": "1.1.0-*",
|
||||
"Microsoft.Extensions.FileProviders.Physical": "1.1.0-*",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "1.1.0-*",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
|
||||
namespace Microsoft.DotNet.Watcher.Tools
|
||||
{
|
||||
internal class CommandLineOptions
|
||||
{
|
||||
public bool IsHelp { get; private set; }
|
||||
public IList<string> RemainingArguments { get; private set; }
|
||||
public static CommandLineOptions Parse(string[] args, TextWriter consoleOutput)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
var app = new CommandLineApplication(throwOnUnexpectedArg: false)
|
||||
{
|
||||
Name = "dotnet watch",
|
||||
FullName = "Microsoft DotNet File Watcher",
|
||||
Out = consoleOutput,
|
||||
AllowArgumentSeparator = true
|
||||
};
|
||||
|
||||
app.HelpOption("-?|-h|--help");
|
||||
|
||||
app.OnExecute(() =>
|
||||
{
|
||||
if (app.RemainingArguments.Count == 0)
|
||||
{
|
||||
app.ShowHelp();
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (app.Execute(args) != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CommandLineOptions
|
||||
{
|
||||
RemainingArguments = app.RemainingArguments,
|
||||
IsHelp = app.IsShowingInformation
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ using System.IO;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Watcher.Core;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.DotNet.Watcher.Tools
|
||||
|
|
@ -14,9 +13,24 @@ namespace Microsoft.DotNet.Watcher.Tools
|
|||
public class Program
|
||||
{
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly TextWriter _out;
|
||||
|
||||
public Program()
|
||||
public Program(TextWriter consoleOutput, CancellationToken cancellationToken)
|
||||
{
|
||||
if (consoleOutput == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(consoleOutput));
|
||||
}
|
||||
|
||||
if (cancellationToken == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cancellationToken));
|
||||
}
|
||||
|
||||
_cancellationToken = cancellationToken;
|
||||
_out = consoleOutput;
|
||||
|
||||
_loggerFactory = new LoggerFactory();
|
||||
|
||||
var logVar = Environment.GetEnvironmentVariable("DOTNET_WATCH_LOG_LEVEL");
|
||||
|
|
@ -44,49 +58,44 @@ namespace Microsoft.DotNet.Watcher.Tools
|
|||
ev.Cancel = false;
|
||||
};
|
||||
|
||||
return new Program().MainInternal(args, ctrlCTokenSource.Token);
|
||||
int exitCode;
|
||||
try
|
||||
{
|
||||
exitCode = new Program(Console.Out, ctrlCTokenSource.Token)
|
||||
.MainInternalAsync(args)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// swallow when only exception is the CTRL+C exit cancellation task
|
||||
exitCode = 0;
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
private int MainInternal(string[] args, CancellationToken cancellationToken)
|
||||
private async Task<int> MainInternalAsync(string[] args)
|
||||
{
|
||||
var app = new CommandLineApplication();
|
||||
app.Name = "dotnet-watch";
|
||||
app.FullName = "Microsoft dotnet File Watcher";
|
||||
|
||||
app.HelpOption("-?|-h|--help");
|
||||
|
||||
app.OnExecute(() =>
|
||||
var options = CommandLineOptions.Parse(args, _out);
|
||||
if (options == null)
|
||||
{
|
||||
var projectToWatch = Path.Combine(Directory.GetCurrentDirectory(), ProjectModel.Project.FileName);
|
||||
var watcher = DotNetWatcher.CreateDefault(_loggerFactory);
|
||||
|
||||
try
|
||||
{
|
||||
watcher.WatchAsync(projectToWatch, args, cancellationToken).Wait();
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
if (ex.InnerExceptions.Count != 1 || !(ex.InnerException is TaskCanceledException))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (args == null ||
|
||||
args.Length == 0 ||
|
||||
args[0].Equals("--help", StringComparison.OrdinalIgnoreCase) ||
|
||||
args[0].Equals("-h", StringComparison.OrdinalIgnoreCase) ||
|
||||
args[0].Equals("-?", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
app.ShowHelp();
|
||||
// invalid args syntax
|
||||
return 1;
|
||||
}
|
||||
|
||||
return app.Execute();
|
||||
if (options.IsHelp)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
var projectToWatch = Path.Combine(Directory.GetCurrentDirectory(), ProjectModel.Project.FileName);
|
||||
|
||||
await DotNetWatcher
|
||||
.CreateDefault(_loggerFactory)
|
||||
.WatchAsync(projectToWatch, options.RemainingArguments, _cancellationToken);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@
|
|||
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: AssemblyMetadata("Serviceable", "True")]
|
||||
[assembly: NeutralResourcesLanguage("en-US")]
|
||||
[assembly: AssemblyCompany("Microsoft Corporation.")]
|
||||
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
|
||||
[assembly: AssemblyProduct("Microsoft .NET")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.DotNet.Watcher.Tools.Tests, PublicKey = 0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.DotNet.Watcher.Tools.Tests, PublicKey = 0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
@ -18,7 +18,9 @@ Add `Microsoft.DotNet.Watcher.Tools` to the `tools` section of your `project.jso
|
|||
|
||||
### How To Use
|
||||
|
||||
dotnet watch [dotnet arguments]
|
||||
dotnet watch [-?|-h|--help]
|
||||
|
||||
dotnet watch [[--] <dotnet arguments>...]
|
||||
|
||||
Add `watch` after `dotnet` in the command that you want to run:
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||
{
|
||||
public class CommandLineOptionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(new object[] { new[] { "-h" } })]
|
||||
[InlineData(new object[] { new[] { "-?" } })]
|
||||
[InlineData(new object[] { new[] { "--help" } })]
|
||||
[InlineData(new object[] { new[] { "--help", "--bogus" } })]
|
||||
[InlineData(new object[] { new[] { "--" } })]
|
||||
[InlineData(new object[] { new string[0] })]
|
||||
public void HelpArgs(string[] args)
|
||||
{
|
||||
var stdout = new StringBuilder();
|
||||
|
||||
var options = CommandLineOptions.Parse(args, new StringWriter(stdout));
|
||||
|
||||
Assert.True(options.IsHelp);
|
||||
Assert.Contains("Usage: dotnet watch ", stdout.ToString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new[] { "run" }, new[] { "run" })]
|
||||
[InlineData(new[] { "run", "--", "subarg" }, new[] { "run", "--", "subarg" })]
|
||||
[InlineData(new[] { "--", "run", "--", "subarg" }, new[] { "run", "--", "subarg" })]
|
||||
[InlineData(new[] { "--unrecognized-arg" }, new[] { "--unrecognized-arg" })]
|
||||
public void ParsesRemainingArgs(string[] args, string[] expected)
|
||||
{
|
||||
var stdout = new StringBuilder();
|
||||
|
||||
var options = CommandLineOptions.Parse(args, new StringWriter(stdout));
|
||||
|
||||
Assert.Equal(expected, options.RemainingArguments.ToArray());
|
||||
Assert.False(options.IsHelp);
|
||||
Assert.Empty(stdout.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>8a2e6961-6b12-4a8e-8215-3e7301d52eac</ProjectGuid>
|
||||
<RootNamespace>Microsoft.DotNet.Watcher.Tools.Tests</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"buildOptions": {
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.DotNet.Watcher.Tools": "1.0.0-*",
|
||||
"dotnet-test-xunit": "2.2.0-*",
|
||||
"xunit": "2.2.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"version": "1.0.0",
|
||||
"type": "platform"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"testRunner": "xunit"
|
||||
}
|
||||
Loading…
Reference in New Issue