.Net7 dotnet watch regression: Failed to create MSBuildWorkspace for *.fsproj
En3Tho opened this issue ยท 17 comments
I'm running BlazorServer app which has a dependency on F# project. Previously with .Net6 there was no problem running applying HotReload and some of stuff was changing instantly (for example applying a css class to blazor markup).
With .Net 7 I'm seeing an error "Cannot_open_project_0_because_the_file_extension_1_is_not_associated_with_a_language" error because only csproj and vbproj are supported.
Now it does full rebuild even if only C# parts are changed. Before it worked fine.
To repro:
Create an empty blazor server project and reference an F# project with some data. Run dotnet watch. Change something trivial like css/markup.
Expected: hot reload works and page is updated immediately
Result: hot reload fails with this error and full rebuild is issued
While debugging dotnet watch in main I've noticed that no LanguageService was registered for F#. C#, VB and TypeScript are there, but F# is missing. I'm not sure how to register this thing.
Afaik, msbuildworkspaces never supported fsproj/shproj projects.
@baronfel @rainersigwald do you know what may have changed in watch?
We'd like to support F# in workspaces, is there anything which can be done?
I'm guessing it used to work before Hot Reload was implemented in dotnet watch. Can you try dotnet watch --no-hot-reload?
dotnet watch --no-hot-reload no longer displays that error but not suprisingly there is no hot reload and every edit results in a rebuild.
I'm guessing it used to work before Hot Reload was implemented in dotnet watch
I'm not sure I correctly understood. Do you mean in Net6 there was no hot-reload in dotnet watch? Did dotnet watch use some other mechanism to apply changes instantly?
Issue magically resolved itself after git clean -xdf . Now this error doesnt get shown and dotnet watch is working as usual. Meaning change C# files -> hot reload applies when possible, change F# file -> rebuild
Oh, no. I'm mistaken. Sorry. It applied a change only once for a static file and then failed to apply C# changes. Issue still persists
One thing I can say is that while debugging 6.0 dotnet-watch I noticed that fsproj references somehow never get added to C# projects. In "main" they do.
I don't know if it's a bug or not but it made 6.0 work
I've trried 2 things:
1: simply ignore msbuild error
2. remove unresolved references from solution object to simulate net6 behavior
Both cases result in:
dotnet watch โ Caught top-level exception from hot reload: System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.Immutable.ImmutableArray`1.get_Item(Int32 index)
at Microsoft.CodeAnalysis.NodeStateTable`1.Single()
at Microsoft.CodeAnalysis.CombineNode`2.UpdateStateTable(Builder graphState, NodeStateTable`1 previousTable, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.DriverStateTable.Builder.GetLatestStateTableForNode[T](IIncrementalGeneratorNode`1 source)
at Microsoft.CodeAnalysis.TransformNode`2.UpdateStateTable(Builder builder, NodeStateTable`1 previousTable, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.DriverStateTable.Builder.GetLatestStateTableForNode[T](IIncrementalGeneratorNode`1 source)
at Microsoft.CodeAnalysis.CombineNode`2.UpdateStateTable(Builder graphState, NodeStateTable`1 previousTable, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.DriverStateTable.Builder.GetLatestStateTableForNode[T](IIncrementalGeneratorNode`1 source)
at Microsoft.CodeAnalysis.CombineNode`2.UpdateStateTable(Builder graphState, NodeStateTable`1 previousTable, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.DriverStateTable.Builder.GetLatestStateTableForNode[T](IIncrementalGeneratorNode`1 source)
at Microsoft.CodeAnalysis.TransformNode`2.UpdateStateTable(Builder builder, NodeStateTable`1 previousTable, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.DriverStateTable.Builder.GetLatestStateTableForNode[T](IIncrementalGeneratorNode`1 source)
at Microsoft.CodeAnalysis.SourceOutputNode`1.UpdateStateTable(Builder graphState, NodeStateTable`1 previousTable, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.DriverStateTable.Builder.GetLatestStateTableForNode[T](IIncrementalGeneratorNode`1 source)
at Microsoft.CodeAnalysis.SourceOutputNode`1.AppendOutputs(IncrementalExecutionContext context, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.GeneratorDriver.UpdateOutputs(ImmutableArray`1 outputNodes, IncrementalGeneratorOutputKind outputKind, Builder generatorRunStateBuilder, CancellationToken cancellationToken, Bui
lder driverStateBuilder)
at Microsoft.CodeAnalysis.GeneratorDriver.RunGeneratorsCore(Compilation compilation, DiagnosticBag diagnosticsBag, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.GeneratorDriver.RunGenerators(Compilation compilation, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.SolutionState.CompilationTracker.FinalizeCompilationAsync(SolutionState solution, Compilation compilationWithoutGenerators, CompilationTrackerGeneratorInfo generatorInfo, Compil
ation compilationWithStaleGeneratedTrees, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.SolutionState.CompilationTracker.BuildFinalStateFromInProgressStateAsync(SolutionState solution, InProgressState state, Compilation inProgressCompilation, CancellationToken canc
ellationToken)
at Microsoft.CodeAnalysis.SolutionState.CompilationTracker.BuildCompilationInfoAsync(SolutionState solution, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.SolutionState.CompilationTracker.GetOrBuildCompilationInfoAsync(SolutionState solution, Boolean lockGate, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.SolutionState.CompilationTracker.GetSourceGeneratedDocumentStatesAsync(SolutionState solution, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.EditAndContinue.EditSession.PopulateChangedAndAddedDocumentsAsync(Project oldProject, Project newProject, ArrayBuilder`1 changedOrAddedDocuments, CancellationToken cancellationT
oken)
at Microsoft.CodeAnalysis.EditAndContinue.EditSession.EmitSolutionUpdateAsync(Solution solution, ActiveStatementSpanProvider solutionActiveStatementSpanProvider, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.EditAndContinue.DebuggingSession.EmitSolutionUpdateAsync(Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.ExternalAccess.Watch.Api.WatchHotReloadService.EmitSolutionUpdateAsync(Solution solution, CancellationToken cancellationToken)
at Microsoft.DotNet.Watcher.Tools.CompilationHandler.TryHandleFileChange(DotNetWatchContext context, FileItem[] files, CancellationToken cancellationToken) in G:\source\repos\dotnet\sdk\src\BuiltInTools\
dotnet-watch\HotReload\CompilationHandler.cs:line 125
at Microsoft.DotNet.Watcher.Tools.HotReload.TryHandleFileChange(DotNetWatchContext context, FileItem[] files, CancellationToken cancellationToken) in G:\source\repos\dotnet\sdk\src\BuiltInTools\dotnet-wa
tch\HotReload\HotReload.cs:line 46
at Microsoft.DotNet.Watcher.HotReloadDotNetWatcher.WatchAsync(DotNetWatchContext context, CancellationToken cancellationToken) in G:\source\repos\dotnet\sdk\src\BuiltInTools\dotnet-watch\HotReloadDotNetW
atcher.cs:line 184
Only table2 here is empty.
I guess this is sorta similar to dotnet/roslyn#54470
What can I do next? Try to manually build CodeServices and tinker with it?
The IndexOutOfRangeException should now be fixed by dotnet/roslyn#65223
@chsienki Thanks. Lines from that PR are missing in decompiled code. I guess I will have to wait until sdk's dependencies go up a little. This gives me some hope bringing back proper behavior.
@chsienki Just in case I can confirm that after updaing sdk's dependencies to 4.5.0-1.22571.11 issue was resolved. Thanks.
@tmat I can confirm that if I simply ignore F# related errors (in this case file type not assocciated with language) watch works. In Net6 it simply didn't report this error for some reason. I don't know why.
The problem is how to separate this error and other errors that could actually fail workspace loading.
There is no real error code, just a string that is already translated at this point. Maybe there are better ways. I don't know.
As a very crude workaround I can only propose something like this:
var workspace = MSBuildWorkspace.Create();
var watchSkipTag = "DotnetWatchIgnoreError";
// assocciate "fsproj" files with a special tag that would be checked later.
// This tag can be added via msbuild properties or we could just bake in fsproj here.
workspace.AssociateFileExtensionWithLanguage("fsproj", watchSkipTag);
workspace.WorkspaceFailed += (_sender, diag) =>
{
if (diag.Diagnostic.Kind == WorkspaceDiagnosticKind.Warning)
{
reporter.Verbose($"MSBuildWorkspace warning: {diag.Diagnostic}");
}
else
{
// simply ignore messages related to this tag as it's most probably Language is not supported error.
if (!diag.Diagnostic.Message.Contains(watchSkipTag))
{
taskCompletionSource.TrySetException(new ApplicationException($"Failed to create MSBuildWorkspace: {diag.Diagnostic}"));
}
}
};@KathleenDollard Hey ! Sorry to ping you but it's .Net 8 soon already and this thing is still broken. Maybe you can help and somehow give this a bit more priority? Simply referencing F# project breaks dotnet watch hot reload mechanism. Even if C# only stuff is edited. It started with .Net 7 and it's been almost a year aleady.
The context: I have a Blazor app that is C# but is using some internal stuff written if F#. Running dotnet watch and simply chaning markup results in a full rebuild. In .Net 6 there was no problem and changes were applied instantly (but it seems to me that it was unintentional)
I've posted a quirky workaround and yes, I can continue to use locally built version but it just seems to me that it is not really cool way for the broader audience.
@tmat can your team take a look? Is hot reload simply incompatible with cross-language references?
@baronfel I may be wrong, but It seems like there is no proper workspace implemented for F#. That's why this error is appearing. The quirky way is to simply ignore F#-related errors. But maybe some kind of workspace shim can be used instead? Something like generic workspace that is suitable for all other projects (not csproj and not vbproj)?
If I do this workaround then everything is back to normal: C# supports hot reload and F# requires restart every time. And this is okay considering there is no F# hot reload support anyway.

