Description
One Pager Link: dotnet/msbuild spec
Decoupling Visual Studio builds of .NET SDK projects
The experience of building a .NET SDK project can differ significantly depending if the project was built using Visla Studio / MSBuild or dotnet build. The build can produce different diagnostics, use different language rules, etc... and that is because building a .NET SDK project from Visual Studio mixes components from MSBuild and the .NET SDK. This means core tooling, like the compiler, can be substantially different between the two types of build. This leads to customer confusion and hard to diagnose problems, as well as increased code workload. To solve this want to ensure that when building a .NET SDK project we use the components from the .NET SDK to do so.
Goals
Consistent end-user experience for build in either DotNet CLI or Visual Studio.
In VS full builds can differ from CLI builds because the compiler and configuration used to perform the build are different. This is a source of user confusion. Changing VS to defer to the SDK entirely helps ensure a more consistent user experience overall.
Decoupling the .NET SDK experience from Visual Studio
Breaking the dependency between the SDK and VS for builds means that VS can better support a 'bring your own SDK' model, which is very often a user expectation in today's software landscape. Users today are very confused by the existing support matrix and this would go a long way towards solving that confusion.
Improve the experience for extensibility authors
Authors of Roslyn Analyzers, Source Generators, MSBuild Tasks, and other build-time extensibility now have a single target environment to aim for, instead of having to deal with multi-targeting pain.
Background/Supporting materials
VS Design-time builds
Here are the components that currently participate in a VS Design-time build:
erDiagram
"Visual Studio" ||--|| "MSBuild DLLs": bundles
"Visual Studio" ||--|| "MSBuild Common.targets": bundles
"Visual Studio" ||--|| "Common Analyzers": bundles
"Visual Studio" ||--|| "Project System(s)": bundles
"Visual Studio" ||--|| "Design-time Targets": bundles
"Visual Studio" ||--|| "Roslyn IDE Components": bundles
"Visual Studio" ||--|| "SDK Resolver": bundles
".NET SDK" ||--|| "SDK Targets": bundles
".NET SDK" ||--|| "SDK Tasks (net472)": bundles
".NET SDK" ||--|| "SDK Analyzers": bundles
"User Repo" ||--|| "global.json": reads
"User Repo" ||--|| "Directory.Build.props": reads
"User Repo" ||--|| "Foo.csproj": reads
Current VS out of proc builds
Here are the components that currently participate in a VS F5 or MSBuild.exe build:
erDiagram
"Visual Studio" ||--|| "MSBuild DLLs": bundles
"Visual Studio" ||--|| "MSBuild Common.targets": bundles
"Visual Studio" ||--|| "Roslyn Compilers": bundles
"Visual Studio" ||--|| "Common Analyzers": bundles
"Visual Studio" ||--|| "SDK Resolver": bundles
".NET SDK" ||--|| "SDK Targets": bundles
".NET SDK" ||--|| "SDK Tasks (net472)": bundles
".NET SDK" ||--|| "SDK Analyzers": bundles
"User Repo" ||--|| "global.json": reads
"User Repo" ||--|| "Directory.Build.props": reads
"User Repo" ||--|| "Foo.csproj": reads
The primary changes are from a DTB perspective are:
- the VS IDE-specific components are not used, and
- the Roslyn Compiler bundled into VS comes into play.
Proposed VS out of proc build changes
Here is the proposed change for VS F5 or MSBuild.exe build:
erDiagram
"Visual Studio" ||--|| "MSBuild DLLs": bundles
"Visual Studio" ||--|| "MSBuild Common.targets": bundles
"Visual Studio" ||--|| "Common Analyzers": bundles
"Visual Studio" ||--|| "SDK Resolver": bundles
".NET SDK" ||--|| "Roslyn Compilers": bundles
".NET SDK" ||--|| "SDK Targets": bundles
".NET SDK" ||--|| "SDK Tasks (.NET)": bundles
".NET SDK" ||--|| "SDK Analyzers": bundles
"User Repo" ||--|| "global.json": reads
"User Repo" ||--|| "Directory.Build.props": reads
"User Repo" ||--|| "Foo.csproj": reads
The primary changes here are
- the compiler used comes from the SDK, and
- The .NET Tasks in the SDK are used instead of the net472 Tasks in the SDK
- NOTE: This is not necessarily a 100% change - usage of .NET Tasks from the SDK is predicated on teams migrating their tasks to use the .NET Core Task Host, which itself is a business/performance/maintainability decision. MSBuild will work to minimize the cost of using .NET Tasks, but there may be some performance impacts.
Importantly - in this scenario the MSBuild engine is still the .NET Framework version inserted into VS.
Impact
- .NET SDK style project builds will be more stable between VS and CLI builds, as the tooling will not be divided between different versions.
- Reduced cost of development for external and internal teams that contribute to Roslyn Analyzers, Source Generators, or MSBuild Tasks.
- End-users will not experience mismatch between analyzer versions, and confirmation that their .NET SDK style builds will behave the same way in VS and in the command line.
Stakeholders
For the internal stakeholder, we have the teams that will continue the work to fully complete the VS and .NET SDK decoupling feature after our base work is done. There are two handovers in this project:
- Enabling the MSBuild.exe execution state to be the same as dotnet command line invocation so the Roslyn team can enable the use of their core compiler in VS.
- Tasks and other projects can be written in .NET Core and the .NET SDK projects will build succesfully in VS. This enables other teams to migrate their tasks to .NET Core instead of keeping them targeting .NET Framework.
The handovers should allow other teams to proceed with their work smoothly and no change in build behavior should be present within MSBuild.
Risks
- Our work is early in the development effort. If this feature is discovered to have too large of an impact in experience of performance the work might be delayed or discarded.
- There might be a performance hit on VS once we start running tasks on .NET Core. It would depending on the amount of non-framework tasks that the project will need to load when opening it in VS. The performance gain from pre-loading will not be available in this scenario.
- There are no concrete deadlines for our part of the feature, but we are aiming for an early preview cycle, so we have a chance to measure the consequences and fix any issues that arise.
Plan
- Ensure that MSBuild.exe provides the same execution state as the dotnet command line invocation.
- This is should take around 1 dev week to complete, and will be handed over to Roslyn team.
- Implement .NET Core task host, tasks can be executed on the .NET Core version instead of .NET framework.
- This should take 1 to 2 dev months to complete, including extensive testing. This would be handed over to internal teams that have custom tasks so they can be updated and tested.
- Load common targets from the .NET SDK and not from .NET Net Framework on VS. This work depends on other team's finalizing their part of the feature and might not be in scope for .NET 10.
- This should take a dev week, if there are no major bugs or regressions.