예제 #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..."));
            }
        }
예제 #2
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..."));
                                }
                            }
                        }
            }
        }
예제 #3
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}");
                            }
                        }
            }
        }