diff --git a/documentation/general/decouple-vs-and-net-sdk.md b/documentation/general/decouple-vs-and-net-sdk.md
index 3391718ced75..7a2964fd48e6 100644
--- a/documentation/general/decouple-vs-and-net-sdk.md
+++ b/documentation/general/decouple-vs-and-net-sdk.md
@@ -119,12 +119,6 @@ Solutions that mix .NET SDK and Visual Studio projects will end up with multiple
The compiler will offer a property that allows SDK projects to use the MSBuild version of the compiler when being built with `msbuild`: `Framework`. This can be added to a `Directory.Build.props` file to impact the entire solution. This is not expected to be a common scenario but is available for customers who need it. This property will be ignored when using `dotnet build` as there is no way to fall back to the Visual Studio compiler in that scenario.
-> [!NOTE]
-> These values are recognized for property `RoslynCompilerType`:
-> - `Core`: use the compiler that comes with the .NET SDK
-> - `Framework`: use the compiler that comes with .NET Framework MSBuild
-> - `FrameworkPackage`: download the Microsoft.Net.Sdk.Compilers.Toolset package which contains the .NET Framework compiler corresponding to the .NET SDK version
-
### .NET Framework based analyzers
There are a few analyzers which are built against .NET Framework TFMs. That means when loaded in a .NET Core compiler it could lead to compatibility issues. This is not expected to be a significant issue as our processes have been pushing customers to target `netstandard` in analyzers for 5+ years. However it is possible that some customers will run into issues here.
@@ -137,6 +131,20 @@ Today there is not a 100% reliable way to shutdown the VBCSCompiler process. The
To mitigate this we will be fixing the `build-server shutdown` command to be reliable across all the scenarios we care about. The details of this are captured in [issue 45956](https://github.com/dotnet/sdk/issues/45956).
+## RoslynCompilerType
+
+Based on the value of the `RoslynCompilerType` property, the SDK sets property `RoslynTasksAssembly` to a full path to a [Roslyn build task DLL][roslyn-build-task],
+and the SDK targets use `$(RoslynTasksAssembly)` to load the build task.
+
+The SDK also sets `RoslynTargetsPath` to the directory path of the roslyn tasks assembly. This property is used by some targets
+but it should be avoided if possible because the tasks assembly name can change as well, not just the directory path.
+
+These values are recognized for property `RoslynCompilerType`:
+- `Core`: use the compiler that comes with the .NET SDK
+- `Framework`: use the compiler that comes with .NET Framework MSBuild
+- `FrameworkPackage`: download the Microsoft.Net.Sdk.Compilers.Toolset package which contains the .NET Framework compiler corresponding to the .NET SDK version
+- `Custom`: the SDK will not override `RoslynTasksAssembly` - used for example by Microsoft.Net.Compilers.Toolset package which injects its own version of the build task
+
## Alternative
### Make the compiler in Visual Studio pluggable
@@ -189,6 +197,7 @@ There is only one version of the DevKit extension. It is released using the late
[matrix-of-paine]: https://aka.ms/dotnet/matrixofpaine
[sdk-lifecycle]: https://learn.microsoft.com/en-us/dotnet/core/porting/versioning-sdk-msbuild-vs#lifecycle
[code-razor-vs-load]: https://github.com/dotnet/roslyn/blob/9aea80927e3d4e5a2846efaa710438c0d8d2bfa2/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs#L1009
+[roslyn-build-task]: https://github.com/dotnet/roslyn/blob/ccb05769e5298ac23c01b33a180a0b3715f53a18/src/Compilers/Core/MSBuildTask/README.md
[setup-dotnet]: https://github.com/actions/setup-dotnet
[pr-detect-torn-state]: https://github.com/dotnet/installer/pull/19144
[issue-analyzer-mt]: https://github.com/dotnet/sdk/issues/20355
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets
index 18f5dbf30a99..64787353cf57 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets
@@ -222,7 +222,7 @@ Copyright (c) .NET Foundation. All rights reserved.
-
diff --git a/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs b/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs
index a665a7e8c697..4ee32f04fb5d 100644
--- a/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs
+++ b/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Reflection;
using System.Runtime.CompilerServices;
using Basic.CompilerLog.Util;
using Microsoft.Build.Logging.StructuredLogger;
+using Microsoft.CodeAnalysis;
namespace Microsoft.NET.Build.Tests;
@@ -51,6 +53,14 @@ public void FullMSBuild_NonSdkStyle(bool useSharedCompilation, Language language
VerifyCompiler(buildCommand, FxCompilerFileName(language), useSharedCompilation);
}
+ [FullMSBuildOnlyTheory, CombinatorialData]
+ public void FullMSBuild_SdkStyle_ToolsetPackage(bool useSharedCompilation, Language language)
+ {
+ var testAsset = CreateProject(useSharedCompilation, language, AddCompilersToolsetPackage);
+ var buildCommand = BuildAndRunUsingMSBuild(testAsset);
+ VerifyCompiler(buildCommand, FxCompilerFileName(language), useSharedCompilation, toolsetPackage: true);
+ }
+
[Theory, CombinatorialData]
public void DotNet(bool useSharedCompilation, Language language)
{
@@ -59,6 +69,14 @@ public void DotNet(bool useSharedCompilation, Language language)
VerifyCompiler(buildCommand, CoreCompilerFileName(language), useSharedCompilation);
}
+ [Theory, CombinatorialData]
+ public void DotNet_ToolsetPackage(bool useSharedCompilation, Language language)
+ {
+ var testAsset = CreateProject(useSharedCompilation, language, AddCompilersToolsetPackage);
+ var buildCommand = BuildAndRunUsingDotNet(testAsset);
+ VerifyCompiler(buildCommand, CoreCompilerFileName(language), useSharedCompilation, toolsetPackage: true);
+ }
+
private TestAsset CreateProject(bool useSharedCompilation, Language language, Action? configure = null, [CallerMemberName] string callingMethod = "")
{
var (projExtension, sourceName, sourceText) = language switch
@@ -102,6 +120,13 @@ End Module
return _testAssetsManager.CreateTestProject(project, callingMethod: callingMethod, targetExtension: projExtension);
}
+ private static void AddCompilersToolsetPackage(TestProject project)
+ {
+ string roslynVersion = typeof(Compilation).Assembly.GetCustomAttribute()!.InformationalVersion.Split('+')[0];
+ Assert.False(string.IsNullOrEmpty(roslynVersion));
+ project.PackageReferences.Add(new TestPackageReference("Microsoft.Net.Compilers.Toolset", roslynVersion));
+ }
+
private TestCommand BuildAndRunUsingMSBuild(TestAsset testAsset)
{
var buildCommand = new MSBuildCommand(testAsset, "Build");
@@ -130,13 +155,23 @@ private void Run(FileInfo outputFile)
.And.HaveStdOut("42");
}
- private static void VerifyCompiler(TestCommand buildCommand, string compilerFileName, bool usedCompilerServer)
+ private static void VerifyCompiler(TestCommand buildCommand, string compilerFileName, bool usedCompilerServer, bool toolsetPackage = false)
{
var binaryLogPath = Path.Join(buildCommand.WorkingDirectory, "msbuild.binlog");
using (var reader = BinaryLogReader.Create(binaryLogPath))
{
var call = reader.ReadAllCompilerCalls().Should().ContainSingle().Subject;
Path.GetFileName(call.CompilerFilePath).Should().Be(compilerFileName);
+
+ const string toolsetPackageName = "microsoft.net.compilers.toolset";
+ if (toolsetPackage)
+ {
+ call.CompilerFilePath.Should().Contain(toolsetPackageName);
+ }
+ else
+ {
+ call.CompilerFilePath.Should().NotContain(toolsetPackageName);
+ }
}
// Verify compiler server message.