Merge aspnet/DotNetTools release/2.2
This commit is contained in:
commit
0782a9dfa9
|
|
@ -12,73 +12,12 @@
|
||||||
"dotnet-sql-cache": {
|
"dotnet-sql-cache": {
|
||||||
"packageTypes": [
|
"packageTypes": [
|
||||||
"DotnetTool"
|
"DotnetTool"
|
||||||
],
|
]
|
||||||
"Exclusions": {
|
|
||||||
"NEUTRAL_RESOURCES_LANGUAGE": {
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"WRONG_PUBLICKEYTOKEN": {
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"ASSEMBLY_INFORMATIONAL_VERSION_MISMATCH": {
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"ASSEMBLY_FILE_VERSION_MISMATCH": {
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"ASSEMBLY_VERSION_MISMATCH": {
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"dotnet-user-secrets": {
|
"dotnet-user-secrets": {
|
||||||
"packageTypes": [
|
"packageTypes": [
|
||||||
"DotnetTool"
|
"DotnetTool"
|
||||||
],
|
]
|
||||||
"Exclusions": {
|
|
||||||
"NEUTRAL_RESOURCES_LANGUAGE": {
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"SERVICING_ATTRIBUTE": {
|
|
||||||
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"WRONG_PUBLICKEYTOKEN": {
|
|
||||||
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"ASSEMBLY_INFORMATIONAL_VERSION_MISMATCH": {
|
|
||||||
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"ASSEMBLY_FILE_VERSION_MISMATCH": {
|
|
||||||
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
},
|
|
||||||
"ASSEMBLY_VERSION_MISMATCH": {
|
|
||||||
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
|
|
||||||
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"dotnet-dev-certs": {
|
"dotnet-dev-certs": {
|
||||||
"packageTypes": [
|
"packageTypes": [
|
||||||
|
|
@ -88,7 +27,7 @@
|
||||||
"Microsoft.AspNetCore.DeveloperCertificates.XPlat": {
|
"Microsoft.AspNetCore.DeveloperCertificates.XPlat": {
|
||||||
"Exclusions": {
|
"Exclusions": {
|
||||||
"DOC_MISSING": {
|
"DOC_MISSING": {
|
||||||
"lib/netcoreapp2.1/Microsoft.AspNetCore.DeveloperCertificates.XPlat.dll": "Docs not required to shipoob package"
|
"lib/netcoreapp2.2/Microsoft.AspNetCore.DeveloperCertificates.XPlat.dll": "Docs not required to shipoob package"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<Description>Package for the CLI first run experience.</Description>
|
<Description>Package for the CLI first run experience.</Description>
|
||||||
<DefineConstants>$(DefineConstants);XPLAT</DefineConstants>
|
<DefineConstants>$(DefineConstants);XPLAT</DefineConstants>
|
||||||
<PackageTags>aspnet;cli</PackageTags>
|
<PackageTags>aspnet;cli</PackageTags>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<Description>Command line tool to generate certificates used in ASP.NET Core during development.</Description>
|
<Description>Command line tool to generate certificates used in ASP.NET Core during development.</Description>
|
||||||
<RootNamespace>Microsoft.AspNetCore.DeveloperCertificates.Tools</RootNamespace>
|
<RootNamespace>Microsoft.AspNetCore.DeveloperCertificates.Tools</RootNamespace>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<Description>Command line tool to create tables and indexes in a Microsoft SQL Server database for distributed caching.</Description>
|
<Description>Command line tool to create tables and indexes in a Microsoft SQL Server database for distributed caching.</Description>
|
||||||
<PackageTags>cache;distributedcache;sqlserver</PackageTags>
|
<PackageTags>cache;distributedcache;sqlserver</PackageTags>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<Description>Command line tool to manage user secrets for Microsoft.Extensions.Configuration.</Description>
|
<Description>Command line tool to manage user secrets for Microsoft.Extensions.Configuration.</Description>
|
||||||
<GenerateUserSecretsAttribute>false</GenerateUserSecretsAttribute>
|
<GenerateUserSecretsAttribute>false</GenerateUserSecretsAttribute>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace Microsoft.Extensions.Configuration.UserSecrets.Tests
|
||||||
private const string ProjectTemplate = @"<Project ToolsVersion=""15.0"" Sdk=""Microsoft.NET.Sdk"">
|
private const string ProjectTemplate = @"<Project ToolsVersion=""15.0"" Sdk=""Microsoft.NET.Sdk"">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
|
||||||
{0}
|
{0}
|
||||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<AssemblyName>Microsoft.Extensions.SecretManager.Tools.Tests</AssemblyName>
|
<AssemblyName>Microsoft.Extensions.SecretManager.Tools.Tests</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,10 @@ Environment variables:
|
||||||
DOTNET_WATCH
|
DOTNET_WATCH
|
||||||
dotnet-watch sets this variable to '1' on all child processes launched.
|
dotnet-watch sets this variable to '1' on all child processes launched.
|
||||||
|
|
||||||
|
DOTNET_WATCH_ITERATION
|
||||||
|
dotnet-watch sets this variable to '1' and increments by one each time
|
||||||
|
a file is changed and the command is restarted.
|
||||||
|
|
||||||
Remarks:
|
Remarks:
|
||||||
The special option '--' is used to delimit the end of the options and
|
The special option '--' is used to delimit the end of the options and
|
||||||
the beginning of arguments that will be passed to the child dotnet process.
|
the beginning of arguments that will be passed to the child dotnet process.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.DotNet.Watcher.Internal;
|
using Microsoft.DotNet.Watcher.Internal;
|
||||||
|
|
@ -32,8 +33,13 @@ namespace Microsoft.DotNet.Watcher
|
||||||
cancellationToken.Register(state => ((TaskCompletionSource<object>) state).TrySetResult(null),
|
cancellationToken.Register(state => ((TaskCompletionSource<object>) state).TrySetResult(null),
|
||||||
cancelledTaskSource);
|
cancelledTaskSource);
|
||||||
|
|
||||||
|
var iteration = 1;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = iteration.ToString(CultureInfo.InvariantCulture);
|
||||||
|
iteration++;
|
||||||
|
|
||||||
var fileSet = await fileSetFactory.CreateAsync(cancellationToken);
|
var fileSet = await fileSetFactory.CreateAsync(cancellationToken);
|
||||||
|
|
||||||
if (fileSet == null)
|
if (fileSet == null)
|
||||||
|
|
@ -69,13 +75,15 @@ namespace Microsoft.DotNet.Watcher
|
||||||
|
|
||||||
await Task.WhenAll(processTask, fileSetTask);
|
await Task.WhenAll(processTask, fileSetTask);
|
||||||
|
|
||||||
if (processTask.Result == 0)
|
if (processTask.Result != 0 && finishedTask == processTask && !cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_reporter.Output("Exited");
|
// Only show this error message if the process exited non-zero due to a normal process exit.
|
||||||
|
// Don't show this if dotnet-watch killed the inner process due to file change or CTRL+C by the user
|
||||||
|
_reporter.Error($"Exited with error code {processTask.Result}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_reporter.Error($"Exited with error code {processTask.Result}");
|
_reporter.Output("Exited");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)
|
if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
{
|
{
|
||||||
internal class DotnetFileWatcher : IFileSystemWatcher
|
internal class DotnetFileWatcher : IFileSystemWatcher
|
||||||
{
|
{
|
||||||
|
private volatile bool _disposed;
|
||||||
|
|
||||||
private readonly Func<string, FileSystemWatcher> _watcherFactory;
|
private readonly Func<string, FileSystemWatcher> _watcherFactory;
|
||||||
|
|
||||||
private FileSystemWatcher _fileSystemWatcher;
|
private FileSystemWatcher _fileSystemWatcher;
|
||||||
|
|
@ -46,6 +48,11 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
|
|
||||||
private void WatcherErrorHandler(object sender, ErrorEventArgs e)
|
private void WatcherErrorHandler(object sender, ErrorEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var exception = e.GetException();
|
var exception = e.GetException();
|
||||||
|
|
||||||
// Win32Exception may be triggered when setting EnableRaisingEvents on a file system type
|
// Win32Exception may be triggered when setting EnableRaisingEvents on a file system type
|
||||||
|
|
@ -62,6 +69,11 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
|
|
||||||
private void WatcherRenameHandler(object sender, RenamedEventArgs e)
|
private void WatcherRenameHandler(object sender, RenamedEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NotifyChange(e.OldFullPath);
|
NotifyChange(e.OldFullPath);
|
||||||
NotifyChange(e.FullPath);
|
NotifyChange(e.FullPath);
|
||||||
|
|
||||||
|
|
@ -79,6 +91,11 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
|
|
||||||
private void WatcherChangeHandler(object sender, FileSystemEventArgs e)
|
private void WatcherChangeHandler(object sender, FileSystemEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NotifyChange(e.FullPath);
|
NotifyChange(e.FullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,15 +115,7 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
{
|
{
|
||||||
enableEvents = _fileSystemWatcher.EnableRaisingEvents;
|
enableEvents = _fileSystemWatcher.EnableRaisingEvents;
|
||||||
|
|
||||||
_fileSystemWatcher.EnableRaisingEvents = false;
|
DisposeInnerWatcher();
|
||||||
|
|
||||||
_fileSystemWatcher.Created -= WatcherChangeHandler;
|
|
||||||
_fileSystemWatcher.Deleted -= WatcherChangeHandler;
|
|
||||||
_fileSystemWatcher.Changed -= WatcherChangeHandler;
|
|
||||||
_fileSystemWatcher.Renamed -= WatcherRenameHandler;
|
|
||||||
_fileSystemWatcher.Error -= WatcherErrorHandler;
|
|
||||||
|
|
||||||
_fileSystemWatcher.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileSystemWatcher = _watcherFactory(BasePath);
|
_fileSystemWatcher = _watcherFactory(BasePath);
|
||||||
|
|
@ -122,6 +131,19 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DisposeInnerWatcher()
|
||||||
|
{
|
||||||
|
_fileSystemWatcher.EnableRaisingEvents = false;
|
||||||
|
|
||||||
|
_fileSystemWatcher.Created -= WatcherChangeHandler;
|
||||||
|
_fileSystemWatcher.Deleted -= WatcherChangeHandler;
|
||||||
|
_fileSystemWatcher.Changed -= WatcherChangeHandler;
|
||||||
|
_fileSystemWatcher.Renamed -= WatcherRenameHandler;
|
||||||
|
_fileSystemWatcher.Error -= WatcherErrorHandler;
|
||||||
|
|
||||||
|
_fileSystemWatcher.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public bool EnableRaisingEvents
|
public bool EnableRaisingEvents
|
||||||
{
|
{
|
||||||
get => _fileSystemWatcher.EnableRaisingEvents;
|
get => _fileSystemWatcher.EnableRaisingEvents;
|
||||||
|
|
@ -130,7 +152,8 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_fileSystemWatcher.Dispose();
|
_disposed = true;
|
||||||
|
DisposeInnerWatcher();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
var stopwatch = new Stopwatch();
|
var stopwatch = new Stopwatch();
|
||||||
|
|
||||||
using (var process = CreateProcess(processSpec))
|
using (var process = CreateProcess(processSpec))
|
||||||
using (var processState = new ProcessState(process))
|
using (var processState = new ProcessState(process, _reporter))
|
||||||
{
|
{
|
||||||
cancellationToken.Register(() => processState.TryKill());
|
cancellationToken.Register(() => processState.TryKill());
|
||||||
|
|
||||||
|
|
@ -97,27 +97,36 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
|
|
||||||
private class ProcessState : IDisposable
|
private class ProcessState : IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly IReporter _reporter;
|
||||||
private readonly Process _process;
|
private readonly Process _process;
|
||||||
private readonly TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();
|
private readonly TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();
|
||||||
private volatile bool _disposed;
|
private volatile bool _disposed;
|
||||||
|
|
||||||
public ProcessState(Process process)
|
public ProcessState(Process process, IReporter reporter)
|
||||||
{
|
{
|
||||||
|
_reporter = reporter;
|
||||||
_process = process;
|
_process = process;
|
||||||
_process.Exited += OnExited;
|
_process.Exited += OnExited;
|
||||||
Task = _tcs.Task.ContinueWith(_ =>
|
Task = _tcs.Task.ContinueWith(_ =>
|
||||||
{
|
{
|
||||||
// We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
|
try
|
||||||
// this code used Process.Exited, which could result in us missing some output due to the ordering of
|
|
||||||
// events.
|
|
||||||
//
|
|
||||||
// See the remarks here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit#System_Diagnostics_Process_WaitForExit_System_Int32_
|
|
||||||
if (!process.WaitForExit(Int32.MaxValue))
|
|
||||||
{
|
{
|
||||||
throw new TimeoutException();
|
// We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
|
||||||
}
|
// this code used Process.Exited, which could result in us missing some output due to the ordering of
|
||||||
|
// events.
|
||||||
|
//
|
||||||
|
// See the remarks here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit#System_Diagnostics_Process_WaitForExit_System_Int32_
|
||||||
|
if (!_process.WaitForExit(Int32.MaxValue))
|
||||||
|
{
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
|
||||||
process.WaitForExit();
|
_process.WaitForExit();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// suppress if this throws if no process is associated with this object anymore.
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,15 +134,26 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
|
|
||||||
public void TryKill()
|
public void TryKill()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_process.HasExited)
|
if (!_process.HasExited)
|
||||||
{
|
{
|
||||||
|
_reporter.Verbose($"Killing process {_process.Id}");
|
||||||
_process.KillTree();
|
_process.KillTree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{ }
|
{
|
||||||
|
_reporter.Verbose($"Error while killing process '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}': {ex.Message}");
|
||||||
|
#if DEBUG
|
||||||
|
_reporter.Verbose(ex.ToString());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExited(object sender, EventArgs args)
|
private void OnExited(object sender, EventArgs args)
|
||||||
|
|
@ -143,8 +163,8 @@ namespace Microsoft.DotNet.Watcher.Internal
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
if (!_disposed)
|
||||||
{
|
{
|
||||||
_disposed = true;
|
|
||||||
TryKill();
|
TryKill();
|
||||||
|
_disposed = true;
|
||||||
_process.Exited -= OnExited;
|
_process.Exited -= OnExited;
|
||||||
_process.Dispose();
|
_process.Dispose();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<Description>Command line tool to watch for source file changes during development and restart the dotnet command.</Description>
|
<Description>Command line tool to watch for source file changes during development and restart the dotnet command.</Description>
|
||||||
<RootNamespace>Microsoft.DotNet.Watcher.Tools</RootNamespace>
|
<RootNamespace>Microsoft.DotNet.Watcher.Tools</RootNamespace>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Dataflow;
|
using System.Threading.Tasks.Dataflow;
|
||||||
using Microsoft.AspNetCore.Testing;
|
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
using Microsoft.Extensions.CommandLineUtils;
|
using Microsoft.Extensions.CommandLineUtils;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
@ -17,16 +17,26 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
{
|
{
|
||||||
private Process _process;
|
private Process _process;
|
||||||
private readonly ProcessSpec _spec;
|
private readonly ProcessSpec _spec;
|
||||||
|
private readonly List<string> _lines;
|
||||||
private BufferBlock<string> _source;
|
private BufferBlock<string> _source;
|
||||||
private ITestOutputHelper _logger;
|
private ITestOutputHelper _logger;
|
||||||
|
private TaskCompletionSource<int> _exited;
|
||||||
|
|
||||||
public AwaitableProcess(ProcessSpec spec, ITestOutputHelper logger)
|
public AwaitableProcess(ProcessSpec spec, ITestOutputHelper logger)
|
||||||
{
|
{
|
||||||
_spec = spec;
|
_spec = spec;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_source = new BufferBlock<string>();
|
_source = new BufferBlock<string>();
|
||||||
|
_lines = new List<string>();
|
||||||
|
_exited = new TaskCompletionSource<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> Output => _lines;
|
||||||
|
|
||||||
|
public Task Exited => _exited.Task;
|
||||||
|
|
||||||
|
public int Id => _process.Id;
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
if (_process != null)
|
if (_process != null)
|
||||||
|
|
@ -52,6 +62,11 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
foreach (var env in _spec.EnvironmentVariables)
|
||||||
|
{
|
||||||
|
_process.StartInfo.EnvironmentVariables[env.Key] = env.Value;
|
||||||
|
}
|
||||||
|
|
||||||
_process.OutputDataReceived += OnData;
|
_process.OutputDataReceived += OnData;
|
||||||
_process.ErrorDataReceived += OnData;
|
_process.ErrorDataReceived += OnData;
|
||||||
_process.Exited += OnExit;
|
_process.Exited += OnExit;
|
||||||
|
|
@ -65,24 +80,30 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
public async Task<string> GetOutputLineAsync(string message, TimeSpan timeout)
|
public async Task<string> GetOutputLineAsync(string message, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
_logger.WriteLine($"Waiting for output line [msg == '{message}']. Will wait for {timeout.TotalSeconds} sec.");
|
_logger.WriteLine($"Waiting for output line [msg == '{message}']. Will wait for {timeout.TotalSeconds} sec.");
|
||||||
return await GetOutputLineAsync(m => message == m).TimeoutAfter(timeout);
|
var cts = new CancellationTokenSource();
|
||||||
|
cts.CancelAfter(timeout);
|
||||||
|
return await GetOutputLineAsync($"[msg == '{message}']", m => string.Equals(m, message, StringComparison.Ordinal), cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetOutputLineStartsWithAsync(string message, TimeSpan timeout)
|
public async Task<string> GetOutputLineStartsWithAsync(string message, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
_logger.WriteLine($"Waiting for output line [msg.StartsWith('{message}')]. Will wait for {timeout.TotalSeconds} sec.");
|
_logger.WriteLine($"Waiting for output line [msg.StartsWith('{message}')]. Will wait for {timeout.TotalSeconds} sec.");
|
||||||
return await GetOutputLineAsync(m => m.StartsWith(message)).TimeoutAfter(timeout);
|
var cts = new CancellationTokenSource();
|
||||||
|
cts.CancelAfter(timeout);
|
||||||
|
return await GetOutputLineAsync($"[msg.StartsWith('{message}')]", m => m != null && m.StartsWith(message, StringComparison.Ordinal), cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GetOutputLineAsync(Predicate<string> predicate)
|
private async Task<string> GetOutputLineAsync(string predicateName, Predicate<string> predicate, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
while (!_source.Completion.IsCompleted)
|
while (!_source.Completion.IsCompleted)
|
||||||
{
|
{
|
||||||
while (await _source.OutputAvailableAsync())
|
while (await _source.OutputAvailableAsync(cancellationToken))
|
||||||
{
|
{
|
||||||
var next = await _source.ReceiveAsync();
|
var next = await _source.ReceiveAsync(cancellationToken);
|
||||||
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'");
|
_lines.Add(next);
|
||||||
if (predicate(next))
|
var match = predicate(next);
|
||||||
|
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'. {(match ? "Matches" : "Does not match")} condition '{predicateName}'.");
|
||||||
|
if (match)
|
||||||
{
|
{
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
@ -92,14 +113,14 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IList<string>> GetAllOutputLines()
|
public async Task<IList<string>> GetAllOutputLinesAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var lines = new List<string>();
|
var lines = new List<string>();
|
||||||
while (!_source.Completion.IsCompleted)
|
while (!_source.Completion.IsCompleted)
|
||||||
{
|
{
|
||||||
while (await _source.OutputAvailableAsync())
|
while (await _source.OutputAvailableAsync(cancellationToken))
|
||||||
{
|
{
|
||||||
var next = await _source.ReceiveAsync();
|
var next = await _source.ReceiveAsync(cancellationToken);
|
||||||
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'");
|
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'");
|
||||||
lines.Add(next);
|
lines.Add(next);
|
||||||
}
|
}
|
||||||
|
|
@ -119,6 +140,8 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
// Wait to ensure the process has exited and all output consumed
|
// Wait to ensure the process has exited and all output consumed
|
||||||
_process.WaitForExit();
|
_process.WaitForExit();
|
||||||
_source.Complete();
|
_source.Complete();
|
||||||
|
_exited.TrySetResult(_process.ExitCode);
|
||||||
|
_logger.WriteLine($"Process {_process.Id} has exited");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
@ -135,6 +158,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
_process.ErrorDataReceived -= OnData;
|
_process.ErrorDataReceived -= OnData;
|
||||||
_process.OutputDataReceived -= OnData;
|
_process.OutputDataReceived -= OnData;
|
||||||
_process.Exited -= OnExit;
|
_process.Exited -= OnExit;
|
||||||
|
_process.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Globalization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
@ -11,10 +13,12 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
{
|
{
|
||||||
public class DotNetWatcherTests : IDisposable
|
public class DotNetWatcherTests : IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly ITestOutputHelper _logger;
|
||||||
private readonly KitchenSinkApp _app;
|
private readonly KitchenSinkApp _app;
|
||||||
|
|
||||||
public DotNetWatcherTests(ITestOutputHelper logger)
|
public DotNetWatcherTests(ITestOutputHelper logger)
|
||||||
{
|
{
|
||||||
|
_logger = logger;
|
||||||
_app = new KitchenSinkApp(logger);
|
_app = new KitchenSinkApp(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,6 +34,37 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
Assert.Equal("1", envValue);
|
Assert.Equal("1", envValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RunsWithIterationEnvVariable()
|
||||||
|
{
|
||||||
|
await _app.StartWatcherAsync();
|
||||||
|
var source = Path.Combine(_app.SourceDirectory, "Program.cs");
|
||||||
|
var contents = File.ReadAllText(source);
|
||||||
|
const string messagePrefix = "DOTNET_WATCH_ITERATION = ";
|
||||||
|
for (var i = 1; i <= 3; i++)
|
||||||
|
{
|
||||||
|
var message = await _app.Process.GetOutputLineStartsWithAsync(messagePrefix, TimeSpan.FromMinutes(2));
|
||||||
|
var count = int.Parse(message.Substring(messagePrefix.Length), CultureInfo.InvariantCulture);
|
||||||
|
Assert.Equal(i, count);
|
||||||
|
|
||||||
|
await _app.IsWaitingForFileChange();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.SetLastWriteTime(source, DateTime.Now);
|
||||||
|
await _app.HasRestarted();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.WriteLine("Retrying. First attempt to restart app failed: " + ex.Message);
|
||||||
|
|
||||||
|
// retry
|
||||||
|
File.SetLastWriteTime(source, DateTime.Now);
|
||||||
|
await _app.HasRestarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_app.Dispose();
|
_app.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -304,42 +304,44 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
|
|
||||||
private void AssertFileChangeRaisesEvent(string directory, IFileSystemWatcher watcher)
|
private void AssertFileChangeRaisesEvent(string directory, IFileSystemWatcher watcher)
|
||||||
{
|
{
|
||||||
var semaphoreSlim = new SemaphoreSlim(0);
|
using (var semaphoreSlim = new SemaphoreSlim(0))
|
||||||
var expectedPath = Path.Combine(directory, Path.GetRandomFileName());
|
|
||||||
EventHandler<string> handler = (object _, string f) =>
|
|
||||||
{
|
{
|
||||||
_output.WriteLine("File changed: " + f);
|
var expectedPath = Path.Combine(directory, Path.GetRandomFileName());
|
||||||
|
EventHandler<string> handler = (object _, string f) =>
|
||||||
|
{
|
||||||
|
_output.WriteLine("File changed: " + f);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.Equals(f, expectedPath, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// There's a known race condition here:
|
||||||
|
// even though we tell the watcher to stop raising events and we unsubscribe the handler
|
||||||
|
// there might be in-flight events that will still process. Since we dispose the reset
|
||||||
|
// event, this code will fail if the handler executes after Dispose happens.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
File.AppendAllText(expectedPath, " ");
|
||||||
|
|
||||||
|
watcher.OnFileChange += handler;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.Equals(f, expectedPath, StringComparison.OrdinalIgnoreCase))
|
// On Unix the file write time is in 1s increments;
|
||||||
{
|
// if we don't wait, there's a chance that the polling
|
||||||
semaphoreSlim.Release();
|
// watcher will not detect the change
|
||||||
}
|
Thread.Sleep(1000);
|
||||||
|
File.AppendAllText(expectedPath, " ");
|
||||||
|
Assert.True(semaphoreSlim.Wait(DefaultTimeout), "Expected a file change event for " + expectedPath);
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
finally
|
||||||
{
|
{
|
||||||
// There's a known race condition here:
|
watcher.OnFileChange -= handler;
|
||||||
// even though we tell the watcher to stop raising events and we unsubscribe the handler
|
|
||||||
// there might be in-flight events that will still process. Since we dispose the reset
|
|
||||||
// event, this code will fail if the handler executes after Dispose happens.
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
File.AppendAllText(expectedPath, " ");
|
|
||||||
|
|
||||||
watcher.OnFileChange += handler;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// On Unix the file write time is in 1s increments;
|
|
||||||
// if we don't wait, there's a chance that the polling
|
|
||||||
// watcher will not detect the change
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
File.AppendAllText(expectedPath, " ");
|
|
||||||
Assert.True(semaphoreSlim.Wait(DefaultTimeout), "Expected a file change event for " + expectedPath);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
watcher.OnFileChange -= handler;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.DotNet.Watcher.Tools.Tests;
|
using Microsoft.DotNet.Watcher.Tools.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -101,7 +102,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
{
|
{
|
||||||
await _app.PrepareAsync();
|
await _app.PrepareAsync();
|
||||||
_app.Start(new [] { "--list" });
|
_app.Start(new [] { "--list" });
|
||||||
var lines = await _app.Process.GetAllOutputLines();
|
var cts = new CancellationTokenSource();
|
||||||
|
cts.CancelAfter(TimeSpan.FromSeconds(30));
|
||||||
|
var lines = await _app.Process.GetAllOutputLinesAsync(cts.Token);
|
||||||
|
var files = lines.Where(l => !l.StartsWith("watch :"));
|
||||||
|
|
||||||
AssertEx.EqualFileList(
|
AssertEx.EqualFileList(
|
||||||
_app.Scenario.WorkFolder,
|
_app.Scenario.WorkFolder,
|
||||||
|
|
@ -111,7 +115,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
"GlobbingApp/include/Foo.cs",
|
"GlobbingApp/include/Foo.cs",
|
||||||
"GlobbingApp/GlobbingApp.csproj",
|
"GlobbingApp/GlobbingApp.csproj",
|
||||||
},
|
},
|
||||||
lines);
|
files);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
|
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
private readonly WatchableApp _app;
|
private readonly WatchableApp _app;
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
public NoDepsAppTests(ITestOutputHelper logger)
|
public NoDepsAppTests(ITestOutputHelper logger)
|
||||||
{
|
{
|
||||||
_app = new WatchableApp("NoDepsApp", logger);
|
_app = new WatchableApp("NoDepsApp", logger);
|
||||||
|
_output = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -33,11 +35,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
File.WriteAllText(fileToChange, programCs);
|
File.WriteAllText(fileToChange, programCs);
|
||||||
|
|
||||||
await _app.HasRestarted();
|
await _app.HasRestarted();
|
||||||
|
Assert.DoesNotContain(_app.Process.Output, l => l.StartsWith("Exited with error code"));
|
||||||
|
|
||||||
var pid2 = await _app.GetProcessId();
|
var pid2 = await _app.GetProcessId();
|
||||||
Assert.NotEqual(pid, pid2);
|
Assert.NotEqual(pid, pid2);
|
||||||
|
|
||||||
// first app should have shut down
|
|
||||||
Assert.Throws<ArgumentException>(() => Process.GetProcessById(pid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -49,10 +50,19 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
await _app.IsWaitingForFileChange();
|
await _app.IsWaitingForFileChange();
|
||||||
|
|
||||||
var fileToChange = Path.Combine(_app.SourceDirectory, "Program.cs");
|
var fileToChange = Path.Combine(_app.SourceDirectory, "Program.cs");
|
||||||
var programCs = File.ReadAllText(fileToChange);
|
|
||||||
File.WriteAllText(fileToChange, programCs);
|
|
||||||
|
|
||||||
await _app.HasRestarted();
|
try
|
||||||
|
{
|
||||||
|
File.SetLastWriteTime(fileToChange, DateTime.Now);
|
||||||
|
await _app.HasRestarted();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// retry
|
||||||
|
File.SetLastWriteTime(fileToChange, DateTime.Now);
|
||||||
|
await _app.HasRestarted();
|
||||||
|
}
|
||||||
|
|
||||||
var pid2 = await _app.GetProcessId();
|
var pid2 = await _app.GetProcessId();
|
||||||
Assert.NotEqual(pid, pid2);
|
Assert.NotEqual(pid, pid2);
|
||||||
await _app.HasExited(); // process should exit after run
|
await _app.HasExited(); // process should exit after run
|
||||||
|
|
|
||||||
|
|
@ -28,23 +28,25 @@ namespace Microsoft.DotNet.Watcher.Tools.Tests
|
||||||
{
|
{
|
||||||
_tempDir
|
_tempDir
|
||||||
.WithCSharpProject("testproj")
|
.WithCSharpProject("testproj")
|
||||||
.WithTargetFrameworks("netcoreapp1.0")
|
.WithTargetFrameworks("netcoreapp2.2")
|
||||||
.Dir()
|
.Dir()
|
||||||
.WithFile("Program.cs")
|
.WithFile("Program.cs")
|
||||||
.Create();
|
.Create();
|
||||||
|
|
||||||
var stdout = new StringBuilder();
|
var output = new StringBuilder();
|
||||||
_console.Out = new StringWriter(stdout);
|
_console.Error = _console.Out = new StringWriter(output);
|
||||||
var program = new Program(_console, _tempDir.Root)
|
using (var app = new Program(_console, _tempDir.Root))
|
||||||
.RunAsync(new[] { "run" });
|
{
|
||||||
|
var run = app.RunAsync(new[] { "run" });
|
||||||
|
|
||||||
await _console.CancelKeyPressSubscribed.TimeoutAfter(TimeSpan.FromSeconds(30));
|
await _console.CancelKeyPressSubscribed.TimeoutAfter(TimeSpan.FromSeconds(30));
|
||||||
_console.ConsoleCancelKey();
|
_console.ConsoleCancelKey();
|
||||||
|
|
||||||
var exitCode = await program.TimeoutAfter(TimeSpan.FromSeconds(30));
|
var exitCode = await run.TimeoutAfter(TimeSpan.FromSeconds(30));
|
||||||
|
|
||||||
Assert.Contains("Shutdown requested. Press Ctrl+C again to force exit.", stdout.ToString());
|
Assert.Contains("Shutdown requested. Press Ctrl+C again to force exit.", output.ToString());
|
||||||
Assert.Equal(0, exitCode);
|
Assert.Equal(0, exitCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -12,7 +11,6 @@ using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Microsoft.Extensions.CommandLineUtils;
|
using Microsoft.Extensions.CommandLineUtils;
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
using Microsoft.Extensions.Tools.Internal;
|
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
||||||
public async Task HasExited()
|
public async Task HasExited()
|
||||||
{
|
{
|
||||||
await Process.GetOutputLineAsync(ExitingMessage, DefaultMessageTimeOut);
|
await Process.GetOutputLineAsync(ExitingMessage, DefaultMessageTimeOut);
|
||||||
await Process.GetOutputLineAsync(WatchExitedMessage, DefaultMessageTimeOut);
|
await Process.GetOutputLineStartsWithAsync(WatchExitedMessage, DefaultMessageTimeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task IsWaitingForFileChange()
|
public async Task IsWaitingForFileChange()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ namespace KitchenSink
|
||||||
Console.WriteLine("Started");
|
Console.WriteLine("Started");
|
||||||
Console.WriteLine("PID = " + Process.GetCurrentProcess().Id);
|
Console.WriteLine("PID = " + Process.GetCurrentProcess().Id);
|
||||||
Console.WriteLine("DOTNET_WATCH = " + Environment.GetEnvironmentVariable("DOTNET_WATCH"));
|
Console.WriteLine("DOTNET_WATCH = " + Environment.GetEnvironmentVariable("DOTNET_WATCH"));
|
||||||
|
Console.WriteLine("DOTNET_WATCH_ITERATION = " + Environment.GetEnvironmentVariable("DOTNET_WATCH_ITERATION"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<AssemblyName>Microsoft.DotNet.Watcher.Tools.Tests</AssemblyName>
|
<AssemblyName>Microsoft.DotNet.Watcher.Tools.Tests</AssemblyName>
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);TestProjects\**\*</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);TestProjects\**\*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -8,14 +8,13 @@ namespace Microsoft.Extensions.Tools.Internal
|
||||||
public static class CliContext
|
public static class CliContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// dotnet --verbose subcommand
|
/// dotnet -d|--diagnostics subcommand
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool IsGlobalVerbose()
|
public static bool IsGlobalVerbose()
|
||||||
{
|
{
|
||||||
bool globalVerbose;
|
bool.TryParse(Environment.GetEnvironmentVariable("DOTNET_CLI_CONTEXT_VERBOSE"), out bool globalVerbose);
|
||||||
bool.TryParse(Environment.GetEnvironmentVariable("DOTNET_CLI_CONTEXT_VERBOSE"), out globalVerbose);
|
|
||||||
return globalVerbose;
|
return globalVerbose;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue