Exemplo n.º 1
0
        public async ValueTask ProcessAsync(DotNetWatchContext context, CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                var arguments = context.Iteration == 0 || (context.ChangedFile?.FilePath is string changedFile && changedFile.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase)) ?
                                new[] { "msbuild", "/t:Build", "/restore", "/nologo" } :
                new[] { "msbuild", "/t:Build", "/nologo" };

                var processSpec = new ProcessSpec
                {
                    Executable       = _muxer,
                    Arguments        = arguments,
                    WorkingDirectory = context.ProcessSpec.WorkingDirectory,
                };

                _reporter.Output("Building...");
                var exitCode = await _processRunner.RunAsync(processSpec, cancellationToken);

                context.FileSet = await _fileSetFactory.CreateAsync(cancellationToken);

                if (exitCode == 0)
                {
                    return;
                }

                // If the build fails, we'll retry until we have a successful build.
                using var fileSetWatcher = new FileSetWatcher(context.FileSet, _reporter);
                await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _reporter.Warn("Waiting for a file to change before restarting dotnet..."));
            }
        }
Exemplo n.º 2
0
        public async ValueTask ProcessAsync(DotNetWatchContext context, CancellationToken cancellationToken)
        {
            using var fileSetWatcher = new FileSetWatcher(context.FileSet, _reporter);
            while (!cancellationToken.IsCancellationRequested)
            {
                var arguments = context.RequiresMSBuildRevaluation ?
                                new[] { "msbuild", "/t:Build", "/restore", "/nologo" } :
                new[] { "msbuild", "/t:Build", "/nologo" };

                var processSpec = new ProcessSpec
                {
                    Executable       = _muxer,
                    Arguments        = arguments,
                    WorkingDirectory = context.ProcessSpec.WorkingDirectory,
                };

                _reporter.Output("Building...");
                var exitCode = await _processRunner.RunAsync(processSpec, cancellationToken);

                if (exitCode == 0)
                {
                    return;
                }

                // If the build fails, we'll retry until we have a successful build.
                context.ChangedFile = await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _reporter.Warn("Waiting for a file to change before restarting dotnet..."));
            }
        }
Exemplo n.º 3
0
        public async Task WatchAsync(DotNetWatchContext context, CancellationToken cancellationToken)
        {
            var processSpec = context.ProcessSpec;

            if (context.SuppressMSBuildIncrementalism)
            {
                _reporter.Verbose("MSBuild incremental optimizations suppressed.");
            }

            _reporter.Output("Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload. " +
                             "Press \"Ctrl + R\" to restart.");

            while (true)
            {
                context.Iteration++;

                for (var i = 0; i < _filters.Length; i++)
                {
                    await _filters[i].ProcessAsync(context, cancellationToken);
                }

                // Reset for next run
                context.RequiresMSBuildRevaluation = false;

                processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = (context.Iteration + 1).ToString(CultureInfo.InvariantCulture);

                var fileSet = context.FileSet;
                if (fileSet == null)
                {
                    _reporter.Error("Failed to find a list of files to watch");
                    return;
                }

                if (!fileSet.Project.IsNetCoreApp60OrNewer())
                {
                    _reporter.Error($"Hot reload based watching is only supported in .NET 6.0 or newer apps. Update the project's launchSettings.json to disable this feature.");
                    return;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                if (context.Iteration == 0)
                {
                    ConfigureExecutable(context, processSpec);
                }

                using var currentRunCancellationSource = new CancellationTokenSource();
                var forceReload = _console.ListenForForceReloadRequest();
                using var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
                          cancellationToken,
                          currentRunCancellationSource.Token,
                          forceReload);
                using var fileSetWatcher = new FileSetWatcher(fileSet, _reporter);
                try
                {
                    using var hotReload = new HotReload(_processRunner, _reporter);
                    await hotReload.InitializeAsync(context, cancellationToken);

                    var processTask = _processRunner.RunAsync(processSpec, combinedCancellationSource.Token);
                    var args        = string.Join(" ", processSpec.Arguments);
                    _reporter.Verbose($"Running {processSpec.ShortDisplayName()} with the following arguments: {args}");

                    _reporter.Output("Started");

                    Task <FileItem?> fileSetTask;
                    Task             finishedTask;

                    while (true)
                    {
                        fileSetTask  = fileSetWatcher.GetChangedFileAsync(combinedCancellationSource.Token);
                        finishedTask = await Task.WhenAny(processTask, fileSetTask).WaitAsync(combinedCancellationSource.Token);

                        if (finishedTask != fileSetTask || fileSetTask.Result is not FileItem fileItem)
                        {
                            // The app exited.
                            break;
                        }
                        else
                        {
                            _reporter.Output($"File changed: {fileItem.FilePath}.");

                            var start = Stopwatch.GetTimestamp();
                            if (await hotReload.TryHandleFileChange(context, fileItem, combinedCancellationSource.Token))
                            {
                                var totalTime = TimeSpan.FromTicks(Stopwatch.GetTimestamp() - start);
                                _reporter.Output($"Hot reload of changes succeeded.");
                                _reporter.Verbose($"Hot reload applied in {totalTime.TotalMilliseconds}ms.");
                            }
                            else
                            {
                                _reporter.Verbose($"Unable to handle changes to {fileItem.FilePath}. Rebuilding the app.");
                                break;
                            }
                        }
                    }

                    // 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 (processTask.Result != 0 && finishedTask == processTask && !cancellationToken.IsCancellationRequested)
                    {
                        // 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
                    {
                        _reporter.Output("Exited");
                    }

                    if (finishedTask == processTask)
                    {
                        // Process exited. Redo evaludation
                        context.RequiresMSBuildRevaluation = true;
                        // Now wait for a file to change before restarting process
                        context.ChangedFile = await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _reporter.Warn("Waiting for a file to change before restarting dotnet..."));
                    }
                    else
                    {
                        Debug.Assert(finishedTask == fileSetTask);
                        var changedFile = fileSetTask.Result;
                        context.ChangedFile = changedFile;
                    }
                }
Exemplo n.º 4
0
        public async Task WatchAsync(DotNetWatchContext context, CancellationToken cancellationToken)
        {
            var cancelledTaskSource = new TaskCompletionSource();

            cancellationToken.Register(state => ((TaskCompletionSource)state).TrySetResult(),
                                       cancelledTaskSource);

            var processSpec      = context.ProcessSpec;
            var initialArguments = processSpec.Arguments.ToArray();

            if (context.SuppressMSBuildIncrementalism)
            {
                _reporter.Verbose("MSBuild incremental optimizations suppressed.");
            }

            while (true)
            {
                context.Iteration++;

                // Reset arguments
                processSpec.Arguments = initialArguments;

                for (var i = 0; i < _filters.Length; i++)
                {
                    await _filters[i].ProcessAsync(context, cancellationToken);
                }

                // Reset for next run
                context.RequiresMSBuildRevaluation = false;

                processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = (context.Iteration + 1).ToString(CultureInfo.InvariantCulture);

                var fileSet = context.FileSet;
                if (fileSet == null)
                {
                    _reporter.Error("Failed to find a list of files to watch");
                    return;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                using (var currentRunCancellationSource = new CancellationTokenSource())
                    using (var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
                               cancellationToken,
                               currentRunCancellationSource.Token))
                        using (var fileSetWatcher = new FileSetWatcher(fileSet, _reporter))
                        {
                            var processTask = _processRunner.RunAsync(processSpec, combinedCancellationSource.Token);
                            var args        = string.Join(" ", processSpec.Arguments);
                            _reporter.Verbose($"Running {processSpec.ShortDisplayName()} with the following arguments: {args}");

                            _reporter.Output("Started");

                            Task <FileItem?> fileSetTask;
                            Task             finishedTask;

                            while (true)
                            {
                                fileSetTask  = fileSetWatcher.GetChangedFileAsync(combinedCancellationSource.Token);
                                finishedTask = await Task.WhenAny(processTask, fileSetTask, cancelledTaskSource.Task);

                                if (finishedTask == fileSetTask &&
                                    fileSetTask.Result is FileItem fileItem &&
                                    await _staticFileHandler.TryHandleFileChange(context, fileItem, combinedCancellationSource.Token))
                                {
                                    // We're able to handle the file change event without doing a full-rebuild.
                                }
Exemplo n.º 5
0
        public async Task WatchAsync(ProcessSpec processSpec, IFileSetFactory fileSetFactory, string replica,
                                     CancellationToken cancellationToken)
        {
            var cancelledTaskSource = new TaskCompletionSource <object>();

            cancellationToken.Register(state => ((TaskCompletionSource <object>)state !).TrySetResult(null !),
                                       cancelledTaskSource);

            var iteration = 1;

            while (true)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = iteration.ToString(CultureInfo.InvariantCulture);
                iteration++;

                var fileSet = await fileSetFactory.CreateAsync(cancellationToken);

                if (fileSet == null)
                {
                    _logger.LogError("watch: Failed to find a list of files to watch");
                    return;
                }

                using (var currentRunCancellationSource = new CancellationTokenSource())
                    using (var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
                               cancellationToken,
                               currentRunCancellationSource.Token))
                        using (var fileSetWatcher = new FileSetWatcher(fileSet, _logger))
                        {
                            var fileSetTask = fileSetWatcher.GetChangedFileAsync(combinedCancellationSource.Token);
                            var processTask = ProcessUtil.RunAsync(processSpec, combinedCancellationSource.Token, throwOnError: false);

                            var args = processSpec.Arguments !;
                            _logger.LogDebug($"Running {processSpec.ShortDisplayName()} with the following arguments: {args}");

                            _logger.LogInformation("watch: {Replica} Started", replica);

                            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 (processTask.Result.ExitCode != 0 && finishedTask == processTask && !cancellationToken.IsCancellationRequested)
                            {
                                // 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
                                _logger.LogError($"watch: Exited with error code {processTask.Result}");
                            }
                            else
                            {
                                _logger.LogInformation("watch: {Replica} Exited", replica);
                            }

                            if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            if (finishedTask == processTask)
                            {
                                // Now wait for a file to change before restarting process
                                await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _logger.LogWarning("Waiting for a file to change before restarting dotnet..."));
                            }

                            if (!string.IsNullOrEmpty(fileSetTask.Result))
                            {
                                _logger.LogInformation($"watch: File changed: {fileSetTask.Result}");
                            }

                            if (processSpec.Build != null)
                            {
                                while (true)
                                {
                                    if (cancellationToken.IsCancellationRequested)
                                    {
                                        break;
                                    }

                                    var exitCode = await processSpec.Build();

                                    if (exitCode == 0)
                                    {
                                        break;
                                        // Build failed, keep retrying builds until successful build.
                                    }

                                    await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _logger.LogWarning("Waiting for a file to change before restarting dotnet..."));
                                }
                            }
                        }
            }
        }
Exemplo n.º 6
0
        public async Task WatchAsync(ProcessSpec processSpec, CancellationToken cancellationToken)
        {
            Ensure.NotNull(processSpec, nameof(processSpec));

            var cancelledTaskSource = new TaskCompletionSource();

            cancellationToken.Register(state => ((TaskCompletionSource)state).TrySetResult(),
                                       cancelledTaskSource);

            var initialArguments = processSpec.Arguments.ToArray();
            var context          = new DotNetWatchContext
            {
                Iteration   = -1,
                ProcessSpec = processSpec,
                Reporter    = _reporter,
                SuppressMSBuildIncrementalism = _dotnetWatchOptions.SuppressMSBuildIncrementalism,
            };

            if (context.SuppressMSBuildIncrementalism)
            {
                _reporter.Verbose("MSBuild incremental optimizations suppressed.");
            }

            while (true)
            {
                context.Iteration++;

                // Reset arguments
                processSpec.Arguments = initialArguments;

                for (var i = 0; i < _filters.Length; i++)
                {
                    await _filters[i].ProcessAsync(context, cancellationToken);
                }

                // Reset for next run
                context.RequiresMSBuildRevaluation = false;

                processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = (context.Iteration + 1).ToString(CultureInfo.InvariantCulture);

                var fileSet = context.FileSet;
                if (fileSet == null)
                {
                    _reporter.Error("Failed to find a list of files to watch");
                    return;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                using (var currentRunCancellationSource = new CancellationTokenSource())
                    using (var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
                               cancellationToken,
                               currentRunCancellationSource.Token))
                        using (var fileSetWatcher = new FileSetWatcher(fileSet, _reporter))
                        {
                            var processTask = _processRunner.RunAsync(processSpec, combinedCancellationSource.Token);
                            var args        = ArgumentEscaper.EscapeAndConcatenate(processSpec.Arguments);
                            _reporter.Verbose($"Running {processSpec.ShortDisplayName()} with the following arguments: {args}");

                            _reporter.Output("Started");

                            Task <FileItem?> fileSetTask;
                            Task             finishedTask;

                            while (true)
                            {
                                fileSetTask  = fileSetWatcher.GetChangedFileAsync(combinedCancellationSource.Token);
                                finishedTask = await Task.WhenAny(processTask, fileSetTask, cancelledTaskSource.Task);

                                if (context.BrowserRefreshServer is not null &&
                                    finishedTask == fileSetTask &&
                                    fileSetTask.Result is FileItem {
                                    FileKind : FileKind.StaticFile
                                } file)
                                {
                                    _reporter.Verbose($"Handling file change event for static content {file.FilePath}.");

                                    // If we can handle the file change without a browser refresh, do it.
                                    await StaticContentHandler.TryHandleFileAction(context.BrowserRefreshServer, file, combinedCancellationSource.Token);
                                }
Exemplo n.º 7
0
        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);

            var iteration = 1;

            while (true)
            {
                processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = iteration.ToString(CultureInfo.InvariantCulture);
                iteration++;

                var fileSet = await fileSetFactory.CreateAsync(cancellationToken);

                if (fileSet == null)
                {
                    _reporter.Error("Failed to find a list of files to watch");
                    return;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                using (var currentRunCancellationSource = new CancellationTokenSource())
                    using (var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
                               cancellationToken,
                               currentRunCancellationSource.Token))
                        using (var fileSetWatcher = new FileSetWatcher(fileSet, _reporter))
                        {
                            var fileSetTask = fileSetWatcher.GetChangedFileAsync(combinedCancellationSource.Token);
                            var processTask = _processRunner.RunAsync(processSpec, combinedCancellationSource.Token);

                            var args = ArgumentEscaper.EscapeAndConcatenate(processSpec.Arguments);
                            _reporter.Verbose($"Running {processSpec.ShortDisplayName()} with the following arguments: {args}");

                            _reporter.Output("Started");

                            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 (processTask.Result != 0 && finishedTask == processTask && !cancellationToken.IsCancellationRequested)
                            {
                                // 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
                            {
                                _reporter.Output("Exited");
                            }

                            if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            if (finishedTask == processTask)
                            {
                                _reporter.Warn("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))
                            {
                                _reporter.Output($"File changed: {fileSetTask.Result}");
                            }
                        }
            }
        }
Exemplo n.º 8
0
        public async Task WatchAsync(ProcessSpec processSpec, CancellationToken cancellationToken)
        {
            Ensure.NotNull(processSpec, nameof(processSpec));

            var cancelledTaskSource = new TaskCompletionSource();

            cancellationToken.Register(state => ((TaskCompletionSource)state).TrySetResult(),
                                       cancelledTaskSource);

            var initialArguments = processSpec.Arguments.ToArray();
            var suppressMSBuildIncrementalism = Environment.GetEnvironmentVariable("DOTNET_WATCH_SUPPRESS_MSBUILD_INCREMENTALISM");
            var context = new DotNetWatchContext
            {
                Iteration   = -1,
                ProcessSpec = processSpec,
                Reporter    = _reporter,
                SuppressMSBuildIncrementalism = suppressMSBuildIncrementalism == "1" || suppressMSBuildIncrementalism == "true",
            };

            if (context.SuppressMSBuildIncrementalism)
            {
                _reporter.Verbose("MSBuild incremental optimizations suppressed.");
            }

            while (true)
            {
                context.Iteration++;

                // Reset arguments
                processSpec.Arguments = initialArguments;

                for (var i = 0; i < _filters.Length; i++)
                {
                    await _filters[i].ProcessAsync(context, cancellationToken);
                }

                // Reset for next run
                context.RequiresMSBuildRevaluation = false;

                processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = (context.Iteration + 1).ToString(CultureInfo.InvariantCulture);

                var fileSet = context.FileSet;
                if (fileSet == null)
                {
                    _reporter.Error("Failed to find a list of files to watch");
                    return;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                using (var currentRunCancellationSource = new CancellationTokenSource())
                    using (var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
                               cancellationToken,
                               currentRunCancellationSource.Token))
                        using (var fileSetWatcher = new FileSetWatcher(fileSet, _reporter))
                        {
                            var fileSetTask = fileSetWatcher.GetChangedFileAsync(combinedCancellationSource.Token);
                            var processTask = _processRunner.RunAsync(processSpec, combinedCancellationSource.Token);

                            var args = ArgumentEscaper.EscapeAndConcatenate(processSpec.Arguments);
                            _reporter.Verbose($"Running {processSpec.ShortDisplayName()} with the following arguments: {args}");

                            _reporter.Output("Started");

                            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 (processTask.Result != 0 && finishedTask == processTask && !cancellationToken.IsCancellationRequested)
                            {
                                // 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
                            {
                                _reporter.Output("Exited");
                            }

                            if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            context.ChangedFile = fileSetTask.Result;
                            if (finishedTask == processTask)
                            {
                                // Process exited. Redo evaludation
                                context.RequiresMSBuildRevaluation = true;
                                // Now wait for a file to change before restarting process
                                context.ChangedFile = await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _reporter.Warn("Waiting for a file to change before restarting dotnet..."));
                            }

                            if (!string.IsNullOrEmpty(fileSetTask.Result))
                            {
                                _reporter.Output($"File changed: {fileSetTask.Result}");
                            }
                        }
            }
        }