aspnetcore/src/Microsoft.DotNet.Watcher.Tools/DotNetWatcher.cs

83 lines
3.4 KiB
C#

// 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.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.Watcher.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher
{
public class DotNetWatcher
{
private readonly ILogger _logger;
private readonly ProcessRunner _processRunner;
public DotNetWatcher(ILogger logger)
{
Ensure.NotNull(logger, nameof(logger));
_logger = logger;
_processRunner = new ProcessRunner(logger);
}
public async Task WatchAsync(ProcessSpec processSpec, IFileSetFactory fileSetFactory, CancellationToken cancellationToken)
{
Ensure.NotNull(processSpec, nameof(processSpec));
var cancelledTaskSource = new TaskCompletionSource<object>();
cancellationToken.Register(state => ((TaskCompletionSource<object>)state).TrySetResult(null), cancelledTaskSource);
while (true)
{
var fileSet = await fileSetFactory.CreateAsync(cancellationToken);
if (cancellationToken.IsCancellationRequested)
{
return;
}
using (var currentRunCancellationSource = new CancellationTokenSource())
using (var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
currentRunCancellationSource.Token))
using (var fileSetWatcher = new FileSetWatcher(fileSet))
{
var fileSetTask = fileSetWatcher.GetChangedFileAsync(combinedCancellationSource.Token);
var processTask = _processRunner.RunAsync(processSpec, combinedCancellationSource.Token);
_logger.LogInformation("Running {execName} with the following arguments: {args}",
processSpec.ShortDisplayName(),
ArgumentEscaper.EscapeAndConcatenate(processSpec.Arguments));
var finishedTask = await Task.WhenAny(processTask, fileSetTask, cancelledTaskSource.Task);
// Regardless of the which task finished first, make sure everything is cancelled
// and wait for dotnet to exit. We don't want orphan processes
currentRunCancellationSource.Cancel();
await Task.WhenAll(processTask, fileSetTask);
if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)
{
return;
}
if (finishedTask == processTask)
{
_logger.LogInformation("Waiting for a file to change before restarting dotnet...");
// Now wait for a file to change before restarting process
await fileSetWatcher.GetChangedFileAsync(cancellationToken);
}
if (!string.IsNullOrEmpty(fileSetTask.Result))
{
_logger.LogInformation($"File changed: {fileSetTask.Result}");
}
}
}
}
}
}