From 20d85a19245625086e606ffdafb6d69d42d44e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 12 Dec 2024 10:28:35 -0800 Subject: [PATCH] Workaround for MSbuildProjectLoader.LoadProjectInfoAsync throwing on unrecognized project language (#44928) --- .../EnvironmentVariablesBuilder.cs | 2 +- .../HotReload/IncrementalMSBuildWorkspace.cs | 12 ++++- .../dotnet-watch/HotReloadDotNetWatcher.cs | 11 ++++ .../HotReload/ApplyDeltaTests.cs | 54 +++++++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs b/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs index da2e1ec10e99..37f17e94c315 100644 --- a/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs +++ b/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs @@ -60,7 +60,7 @@ public void SetVariable(string name, string value) public void ConfigureProcess(ProcessSpec processSpec) { - processSpec.Arguments = [.. GetCommandLineDirectives(), .. processSpec.Arguments]; + processSpec.Arguments = [.. GetCommandLineDirectives(), .. processSpec.Arguments ?? []]; AddToEnvironment(processSpec.EnvironmentVariables); } diff --git a/src/BuiltInTools/dotnet-watch/HotReload/IncrementalMSBuildWorkspace.cs b/src/BuiltInTools/dotnet-watch/HotReload/IncrementalMSBuildWorkspace.cs index 90ec68694946..d508f7338bd9 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/IncrementalMSBuildWorkspace.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/IncrementalMSBuildWorkspace.cs @@ -38,7 +38,17 @@ public async Task UpdateProjectConeAsync(string rootProjectPath, CancellationTok var loader = new MSBuildProjectLoader(this); var projectMap = ProjectMap.Create(); - var projectInfos = await loader.LoadProjectInfoAsync(rootProjectPath, projectMap, progress: null, msbuildLogger: null, cancellationToken).ConfigureAwait(false); + + ImmutableArray projectInfos; + try + { + projectInfos = await loader.LoadProjectInfoAsync(rootProjectPath, projectMap, progress: null, msbuildLogger: null, cancellationToken).ConfigureAwait(false); + } + catch (InvalidOperationException) + { + // TODO: workaround for https://github.com/dotnet/roslyn/issues/75956 + projectInfos = []; + } var oldProjectIdsByPath = oldSolution.Projects.ToDictionary(keySelector: static p => p.FilePath!, elementSelector: static p => p.Id); diff --git a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs index da4f09764958..fd40213ace83 100644 --- a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs +++ b/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs @@ -91,6 +91,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke var rootProjectOptions = Context.RootProjectOptions; var rootProjectNode = Context.ProjectGraph.GraphRoots.Single(); + var rootProjectCapabilities = rootProjectNode.GetCapabilities(); await using var runtimeProcessLauncher = _runtimeProcessLauncherFactory?.TryCreate(rootProjectNode, projectLauncher, rootProjectOptions.BuildProperties); if (runtimeProcessLauncher != null) @@ -211,6 +212,16 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke await compilationHandler.Workspace.UpdateFileContentAsync(changedFiles, iterationCancellationToken); } + if (!rootProjectCapabilities.Contains("SupportsHotReload")) + { + Context.Reporter.Warn($"Project '{rootProjectNode.GetDisplayName()}' does not support Hot Reload and must be rebuilt."); + + // file change already detected + waitForFileChangeBeforeRestarting = false; + iterationCancellationSource.Cancel(); + break; + } + HotReloadEventSource.Log.HotReloadStart(HotReloadEventSource.StartType.Main); var stopwatch = Stopwatch.StartNew(); diff --git a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs index 2ad317fad6ba..23f3076915a9 100644 --- a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs +++ b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs @@ -68,6 +68,60 @@ public static void Print() await App.AssertOutputLineStartsWith("Changed!"); } + [Fact] + public async Task ChangeFileInFSharpProject() + { + var testAsset = TestAssets.CopyTestAsset("FSharpTestAppSimple") + .WithSource(); + + App.Start(testAsset, []); + + await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForFileChangeBeforeRestarting); + + UpdateSourceFile(Path.Combine(testAsset.Path, "Program.fs"), content => content.Replace("Hello World!", "")); + + await App.AssertOutputLineStartsWith(""); + } + + [Fact] + public async Task ChangeFileInFSharpProjectWithLoop() + { + var testAsset = TestAssets.CopyTestAsset("FSharpTestAppSimple") + .WithSource(); + + var source = """ + module ConsoleApplication.Program + + open System + open System.Threading + + [] + let main argv = + while true do + printfn "Waiting" + Thread.Sleep(200) + 0 + """; + + var sourcePath = Path.Combine(testAsset.Path, "Program.fs"); + + File.WriteAllText(sourcePath, source); + + App.Start(testAsset, []); + + await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForChanges); + + UpdateSourceFile(sourcePath, content => content.Replace("Waiting", "")); + + await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForChanges, failure: _ => false); + await App.AssertOutputLineStartsWith(""); + + UpdateSourceFile(sourcePath, content => content.Replace("", "")); + + await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForChanges, failure: _ => false); + await App.AssertOutputLineStartsWith(""); + } + // Test is timing out on .NET Framework: https://github.com/dotnet/sdk/issues/41669 [CoreMSBuildOnlyFact] public async Task HandleTypeLoadFailure()