Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Commit 7c24650

Browse files
author
Nate McMaster
committed
Add support for '--' argument separator
Update README.md
1 parent 6eec856 commit 7c24650

File tree

11 files changed

+215
-67
lines changed

11 files changed

+215
-67
lines changed

Microsoft.DotNet.Watcher.Tools.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 14
4-
VisualStudioVersion = 14.0.25123.0
4+
VisualStudioVersion = 14.0.25420.1
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{66517987-2A5A-4330-B130-207039378FD4}"
77
EndProject
@@ -29,6 +29,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AppWithDeps", "test\TestApp
2929
EndProject
3030
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dependency", "test\TestApps\Dependency\Dependency.xproj", "{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}"
3131
EndProject
32+
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}"
33+
EndProject
3234
Global
3335
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3436
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +65,10 @@ Global
6365
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
6466
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
6567
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Release|Any CPU.Build.0 = Release|Any CPU
68+
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69+
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
70+
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
71+
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC}.Release|Any CPU.Build.0 = Release|Any CPU
6672
EndGlobalSection
6773
GlobalSection(SolutionProperties) = preSolution
6874
HideSolutionNode = FALSE
@@ -76,5 +82,6 @@ Global
7682
{2AB1A28B-2022-49EA-AF77-AC8A875915CC} = {2876B12E-5841-4792-85A8-2929AEE11885}
7783
{F7734E61-F510-41E0-AD15-301A64081CD1} = {2876B12E-5841-4792-85A8-2929AEE11885}
7884
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA} = {2876B12E-5841-4792-85A8-2929AEE11885}
85+
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
7986
EndGlobalSection
8087
EndGlobal

README.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,31 @@ dotnet-watch
22
===
33
`dotnet-watch` is a file watcher for `dotnet` that restarts the specified application when changes in the source code are detected.
44

5+
[![Travis build status](https://img.shields.io/travis/aspnet/dotnet-watch.svg?label=travis-ci&branch=dev&style=flat-square)](https://travis-ci.org/aspnet/dotnet-watch)
6+
[![AppVeyor build status](https://img.shields.io/appveyor/ci/aspnetci/dnx-watch/dev.svg?label=appveyor&style=flat-square)](https://ci.appveyor.com/project/aspnetci/dnx-watch/branch/dev)
7+
8+
[![NuGet stable build](https://img.shields.io/nuget/v/Microsoft.DotNet.Watcher.Tools.svg?style=flat-square&label=stable)](https://www.nuget.org/packages/Microsoft.DotNet.Watcher.Tools/)
9+
[![MyGet nightly build](https://img.shields.io/dotnet.myget/aspnetcore-dev/vpre/Microsoft.DotNet.Watcher.Tools.svg?style=flat-square&label=nightly)](https://dotnet.myget.org/feed/aspnetcore-dev/package/nuget/Microsoft.DotNet.Watcher.Tools)
10+
511
### How To Install
612

713
Add `Microsoft.DotNet.Watcher.Tools` to the `tools` section of your `project.json` file:
814

9-
```
15+
```json
1016
{
1117
...
1218
"tools": {
13-
"Microsoft.DotNet.Watcher.Tools": {
14-
"version": "1.0.0-*",
15-
"imports": "portable-net451+win8"
16-
}
19+
"Microsoft.DotNet.Watcher.Tools": "1.0.0-*"
1720
}
18-
...
1921
}
2022
```
2123

2224
### How To Use
2325

24-
dotnet watch [dotnet arguments]
26+
dotnet watch [[--] <dotnet arguments>...]
2527

2628
Add `watch` after `dotnet` in the command that you want to run:
27-
29+
2830
| What you want to run | Dotnet watch command |
2931
| ---------------------------------------------- | -------------------------------------------------------- |
3032
| dotnet run | dotnet **watch** run |
@@ -41,8 +43,4 @@ Configuration options can be passed to `dotnet watch` through environment variab
4143
| DOTNET_USE_POLLING_FILE_WATCHER | If set to "1" or "true", `dotnet watch` will use a polling file watcher instead of CoreFx's `FileSystemWatcher`. Used when watching files on network shares or Docker mounted volumes. |
4244
| DOTNET_WATCH_LOG_LEVEL | Used to set the logging level for messages coming from `dotnet watch`. Accepted values `None`, `Trace`, `Debug`, `Information`, `Warning`, `Error`, `Critical`. Default: `Information`. |
4345

44-
AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/fxhto3omtehio3aj/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/dnx-watch/branch/dev)
45-
46-
Travis: [![Travis](https://travis-ci.org/aspnet/dotnet-watch.svg?branch=dev)](https://travis-ci.org/aspnet/dotnet-watch)
47-
4846
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.

src/Microsoft.DotNet.Watcher.Core/DotNetWatcher.cs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.IO;
67
using System.Threading;
78
using System.Threading.Tasks;
9+
using Microsoft.DotNet.Cli.Utils;
810
using Microsoft.DotNet.Watcher.Core.Internal;
911
using Microsoft.Extensions.Logging;
1012

@@ -33,7 +35,7 @@ public DotNetWatcher(
3335
_logger = _loggerFactory.CreateLogger(nameof(DotNetWatcher));
3436
}
3537

36-
public async Task WatchAsync(string projectFile, string[] dotnetArguments, CancellationToken cancellationToken)
38+
public async Task WatchAsync(string projectFile, IEnumerable<string> dotnetArguments, CancellationToken cancellationToken)
3739
{
3840
if (string.IsNullOrEmpty(projectFile))
3941
{
@@ -48,24 +50,7 @@ public async Task WatchAsync(string projectFile, string[] dotnetArguments, Cance
4850
throw new ArgumentNullException(nameof(cancellationToken));
4951
}
5052

51-
// If any argument has spaces then quote it because we're going to convert everything
52-
// to string
53-
for (var i = 0; i < dotnetArguments.Length; i++)
54-
{
55-
var arg = dotnetArguments[i];
56-
foreach (char c in arg)
57-
{
58-
if (c == ' ' ||
59-
c == '\t')
60-
{
61-
arg = $"\"{arg}\"";
62-
break;
63-
}
64-
}
65-
dotnetArguments[i] = arg;
66-
}
67-
68-
var dotnetArgumentsAsString = string.Join(" ", dotnetArguments);
53+
var dotnetArgumentsAsString = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(dotnetArguments);
6954

7055
var workingDir = Path.GetDirectoryName(projectFile);
7156

src/Microsoft.DotNet.Watcher.Core/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
},
1818
"dependencies": {
1919
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
20+
"Microsoft.DotNet.Cli.Utils": "1.0.0-*",
2021
"Microsoft.Extensions.FileProviders.Abstractions": "1.1.0-*",
2122
"Microsoft.Extensions.FileProviders.Physical": "1.1.0-*",
2223
"Microsoft.Extensions.Logging.Abstractions": "1.1.0-*",
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using Microsoft.Extensions.CommandLineUtils;
8+
9+
namespace Microsoft.DotNet.Watcher.Tools
10+
{
11+
internal class CommandLineOptions
12+
{
13+
public bool IsHelp { get; set; }
14+
public IList<string> RemainingArguments { get; set; }
15+
public static CommandLineOptions Parse(string[] args, TextWriter consoleOutput)
16+
{
17+
if (args == null)
18+
{
19+
throw new ArgumentNullException(nameof(args));
20+
}
21+
22+
var app = new CommandLineApplication(throwOnUnexpectedArg: false)
23+
{
24+
Name = "dotnet watch",
25+
FullName = "Microsoft DotNet File Watcher",
26+
Out = consoleOutput,
27+
AllowArgumentSeparator = true
28+
};
29+
30+
app.HelpOption("-?|-h|--help");
31+
32+
var options = new CommandLineOptions();
33+
34+
app.OnExecute(() =>
35+
{
36+
if (app.RemainingArguments.Count == 0)
37+
{
38+
app.ShowHelp();
39+
}
40+
41+
return 0;
42+
});
43+
44+
if (app.Execute(args) != 0)
45+
{
46+
return null;
47+
}
48+
49+
options.RemainingArguments = app.RemainingArguments;
50+
options.IsHelp = app.IsShowingInformation;
51+
52+
return options;
53+
}
54+
}
55+
}

src/Microsoft.DotNet.Watcher.Tools/Program.cs

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,39 @@
33

44
using System;
55
using System.IO;
6+
using System.Linq;
67
using System.Threading;
78
using System.Threading.Tasks;
89
using Microsoft.DotNet.Watcher.Core;
9-
using Microsoft.Extensions.CommandLineUtils;
1010
using Microsoft.Extensions.Logging;
1111

1212
namespace Microsoft.DotNet.Watcher.Tools
1313
{
1414
public class Program
1515
{
1616
private readonly ILoggerFactory _loggerFactory;
17+
private readonly TextWriter _error;
18+
private readonly TextWriter _out;
1719

1820
public Program()
21+
:this(Console.Out, Console.Error)
22+
{ }
23+
24+
internal Program(TextWriter consoleOutput, TextWriter consoleError)
1925
{
26+
if (consoleOutput == null)
27+
{
28+
throw new ArgumentNullException(nameof(consoleOutput));
29+
}
30+
31+
if (consoleError == null)
32+
{
33+
throw new ArgumentNullException(nameof(consoleError));
34+
}
35+
36+
_out = consoleOutput;
37+
_error = consoleError;
38+
2039
_loggerFactory = new LoggerFactory();
2140

2241
var logVar = Environment.GetEnvironmentVariable("DOTNET_WATCH_LOG_LEVEL");
@@ -50,43 +69,31 @@ public static int Main(string[] args)
5069

5170
private int MainInternal(string[] args, CancellationToken cancellationToken)
5271
{
53-
var app = new CommandLineApplication();
54-
app.Name = "dotnet-watch";
55-
app.FullName = "Microsoft dotnet File Watcher";
56-
57-
app.HelpOption("-?|-h|--help");
72+
var options = CommandLineOptions.Parse(args, _out);
73+
if (options == null)
74+
{
75+
_error.WriteLine("Unexpected error parsing command line arguments");
76+
return 1;
77+
}
5878

59-
app.OnExecute(() =>
79+
if (options.IsHelp)
6080
{
61-
var projectToWatch = Path.Combine(Directory.GetCurrentDirectory(), ProjectModel.Project.FileName);
62-
var watcher = DotNetWatcher.CreateDefault(_loggerFactory);
81+
return 2;
82+
}
6383

64-
try
65-
{
66-
watcher.WatchAsync(projectToWatch, args, cancellationToken).Wait();
67-
}
68-
catch (AggregateException ex)
69-
{
70-
if (ex.InnerExceptions.Count != 1 || !(ex.InnerException is TaskCanceledException))
71-
{
72-
throw;
73-
}
74-
}
75-
76-
return 0;
77-
});
78-
79-
if (args == null ||
80-
args.Length == 0 ||
81-
args[0].Equals("--help", StringComparison.OrdinalIgnoreCase) ||
82-
args[0].Equals("-h", StringComparison.OrdinalIgnoreCase) ||
83-
args[0].Equals("-?", StringComparison.OrdinalIgnoreCase))
84+
var projectToWatch = Path.Combine(Directory.GetCurrentDirectory(), ProjectModel.Project.FileName);
85+
var watcher = DotNetWatcher.CreateDefault(_loggerFactory);
86+
87+
try
8488
{
85-
app.ShowHelp();
86-
return 1;
89+
watcher.WatchAsync(projectToWatch, options.RemainingArguments, cancellationToken).Wait();
90+
}
91+
catch (AggregateException ex) when (ex.InnerExceptions.Count == 1 && ex.InnerException is TaskCanceledException)
92+
{
93+
// swallow when only exception is the CTRL+C exit cancellation task
8794
}
8895

89-
return app.Execute();
96+
return 0;
9097
}
9198
}
9299
}

src/Microsoft.DotNet.Watcher.Tools/Properties/AssemblyInfo.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33

44
using System.Reflection;
55
using System.Resources;
6-
using System.Runtime.CompilerServices;
76

87
[assembly: AssemblyMetadata("Serviceable", "True")]
98
[assembly: NeutralResourcesLanguage("en-US")]
109
[assembly: AssemblyCompany("Microsoft Corporation.")]
1110
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
1211
[assembly: AssemblyProduct("Microsoft .NET")]
13-
14-
[assembly: InternalsVisibleTo("Microsoft.DotNet.Watcher.Tools.Tests, PublicKey = 0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
[assembly: InternalsVisibleTo("Microsoft.DotNet.Watcher.Tools.Tests, PublicKey = 0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
using Xunit;
8+
9+
namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
10+
{
11+
public class CommandLineOptionsTests
12+
{
13+
[Theory]
14+
[InlineData(new object[] { new[] { "-h" } })]
15+
[InlineData(new object[] { new[] { "-?" } })]
16+
[InlineData(new object[] { new[] { "--help" } })]
17+
[InlineData(new object[] { new[] { "--" } })]
18+
[InlineData(new object[] { new string[0] })]
19+
public void HelpArgs(string[] args)
20+
{
21+
// arrange
22+
var stdout = new StringBuilder();
23+
24+
// act
25+
var options = CommandLineOptions.Parse(args, new StringWriter(stdout));
26+
27+
// assert
28+
Assert.True(options.IsHelp);
29+
Assert.Contains("Usage: dotnet watch ", stdout.ToString());
30+
}
31+
32+
[Theory]
33+
[InlineData(new[] { "run" }, new[] { "run" })]
34+
[InlineData(new[] { "run", "--", "subarg" }, new[] { "run", "--", "subarg" })]
35+
[InlineData(new[] { "--", "run", "--", "subarg" }, new[] { "run", "--", "subarg" })]
36+
[InlineData(new[] { "--unrecognized-arg" }, new[] { "--unrecognized-arg" })]
37+
public void ParsesRemainingArgs(string[] args, string[] expected)
38+
{
39+
// arrange
40+
var stdout = new StringBuilder();
41+
42+
// act
43+
var options = CommandLineOptions.Parse(args, new StringWriter(stdout));
44+
45+
// assert
46+
Assert.Equal(expected, options.RemainingArguments.ToArray());
47+
Assert.False(options.IsHelp);
48+
Assert.Empty(stdout.ToString());
49+
}
50+
}
51+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion>
5+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
6+
</PropertyGroup>
7+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
8+
<PropertyGroup Label="Globals">
9+
<ProjectGuid>8a2e6961-6b12-4a8e-8215-3e7301d52eac</ProjectGuid>
10+
<RootNamespace>Microsoft.DotNet.Watcher.Tools.Tests</RootNamespace>
11+
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
12+
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
13+
</PropertyGroup>
14+
15+
<PropertyGroup>
16+
<SchemaVersion>2.0</SchemaVersion>
17+
</PropertyGroup>
18+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
19+
</Project>

0 commit comments

Comments
 (0)