diff --git a/.vscode/launch.json b/.vscode/launch.json index e05a5afd5d334..3294ce97d0757 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -70,7 +70,27 @@ "program": "C:/Program Files/Microsoft Visual Studio/2022/Preview/MSBuild/Current/Bin/amd64/MSBuild.exe", "args": [ "-restore", + "-p:RoslynCompilerType=Custom", "-p:RoslynTargetsPath=${workspaceFolder}/artifacts/bin/Microsoft.Net.Compilers.Toolset.Package/Debug/tasks/net472", + "-t:Rebuild", + ], + // A simple project that can be used to debug the build tasks against. + "cwd": "${workspaceFolder}/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator", + "stopAtEntry": false, + "console": "internalConsole" + }, + { + "name": "Launch Microsoft.Build.Tasks.CodeAnalysis.dll via MSBuild.exe (netfx bridge)", + "type": "clr", + "request": "launch", + "preLaunchTask": "build toolset", + "program": "C:/Program Files/Microsoft Visual Studio/2022/Preview/MSBuild/Current/Bin/amd64/MSBuild.exe", + "args": [ + "-restore", + "-p:RoslynCompilerType=Custom", + "-p:RoslynTargetsPath=${workspaceFolder}/artifacts/bin/Microsoft.Net.Compilers.Toolset.Package/Debug/tasks/net472", + "-p:RoslynTasksAssembly=${workspaceFolder}/artifacts/bin/Microsoft.Net.Compilers.Toolset.Package/Debug/tasks/netcore/binfx/Microsoft.Build.Tasks.CodeAnalysis.Sdk.dll", + "-t:Rebuild", ], // A simple project that can be used to debug the build tasks against. "cwd": "${workspaceFolder}/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator", @@ -85,7 +105,9 @@ "program": "C:/Program Files/Microsoft Visual Studio/2022/Preview/MSBuild/Current/Bin/amd64/MSBuild.exe", "args": [ "-restore", + "-p:RoslynCompilerType=Custom", "-p:RoslynTargetsPath=${workspaceFolder}/artifacts/bin/Microsoft.Net.Compilers.Toolset.Package/Debug/tasks/netcore", + "-t:Rebuild", ], // A simple project that can be used to debug the build tasks against. "cwd": "${workspaceFolder}/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator", @@ -100,7 +122,9 @@ "program": "dotnet", "args": [ "build", + "-p:RoslynCompilerType=Custom", "-p:RoslynTargetsPath=${workspaceFolder}/artifacts/bin/Microsoft.Net.Compilers.Toolset.Package/Debug/tasks/netcore", + "-t:Rebuild", ], // A simple project that can be used to debug the build tasks against. "cwd": "${workspaceFolder}/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator", diff --git a/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs b/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs index ec44dae4e34ec..9bce6e962ca3d 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs @@ -11,7 +11,6 @@ using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using Microsoft.CodeAnalysis.CommandLine; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.BuildTasks @@ -48,8 +47,12 @@ public abstract class ManagedToolTask : ToolTask /// explicitly overridden. So, if both ToolPath is unset and /// ToolExe == ToolName, we know nothing is overridden, and /// we can use our own csc. + /// + /// We want to continue using the builtin tool if user sets = csc.exe, + /// regardless of whether apphosts will be used or not (in the former case, could be csc.dll), + /// hence we also compare with in addition to . /// - protected bool UsingBuiltinTool => string.IsNullOrEmpty(ToolPath) && ToolExe == ToolName; + protected bool UsingBuiltinTool => string.IsNullOrEmpty(ToolPath) && (ToolExe == ToolName || ToolExe == AppHostToolName); /// /// Is the builtin tool executed by this task running on .NET Core? @@ -75,6 +78,8 @@ private bool UseAppHost } } + internal bool UseAppHost_TestOnly { set => _useAppHost = value; } + protected ManagedToolTask(ResourceManager resourceManager) : base(resourceManager) { @@ -144,6 +149,12 @@ protected sealed override string GenerateFullPathToTool() { if (UsingBuiltinTool) { + // Even if we return full path to tool as "C:\Program Files\dotnet\dotnet.exe" for example, + // an MSBuild logic (caller of this function) can turn that into "C:\Program Files\dotnet\csc.exe" if `ToolExe` is set explicitly to "csc.exe". + // Resetting `ToolExe` to `null` skips that logic. That is a correct thing to do since we already checked `UsingBuiltinTool` + // which means `ToolExe` is not really overridden by user (yes, the user sets it but basically to its default value). + ToolExe = null; + return UseAppHost ? PathToBuiltInTool : RuntimeHostInfo.GetDotNetPathOrDefault(); } @@ -162,7 +173,7 @@ protected sealed override string GenerateFullPathToTool() /// /// ToolName is only used in cases where returns true. /// It returns the name of the managed assembly, which might not be the path returned by - /// GenerateFullPathToTool, which can return the path to e.g. the dotnet executable. + /// , which can return the path to e.g. the dotnet executable. /// protected sealed override string ToolName { @@ -210,7 +221,7 @@ protected static ITaskItem[] GenerateCommandLineArgsTaskItems(List comma return items; } - private static string GetToolDirectory() + internal static string GetToolDirectory() { var buildTaskDirectory = GetBuildTaskDirectory(); #if NET diff --git a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs index 92e7fb4a1e561..3a00e57c87e20 100644 --- a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs @@ -494,6 +494,30 @@ public void EmptyCscToolExe() AssertEx.Equal(Path.Combine("path", "to", "custom_csc", $"csc{PlatformInformation.ExeExtension}"), csc.GeneratePathToTool()); } + /// + /// Setting ToolExe to "csc.exe" should use the built-in compiler regardless of apphost being used or not. + /// + [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2615118")] + public void BuiltInToolExe(bool useAppHost, bool setToolExe) + { + var csc = new Csc(); + csc.UseAppHost_TestOnly = useAppHost; + if (setToolExe) + { + csc.ToolExe = $"csc{PlatformInformation.ExeExtension}"; + } + if (useAppHost) + { + AssertEx.Equal(csc.PathToBuiltInTool, csc.GeneratePathToTool()); + AssertEx.Equal("", csc.GenerateCommandLineContents()); + } + else + { + AssertEx.Equal(RuntimeHostInfo.GetDotNetPathOrDefault(), csc.GeneratePathToTool()); + AssertEx.Equal(RuntimeHostInfo.GetDotNetExecCommandLine(csc.PathToBuiltInTool, ""), csc.GenerateCommandLineContents()); + } + } + [Fact] public void EditorConfig() { diff --git a/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs b/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs index 68504bcce60c3..7e84c77863bb9 100644 --- a/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs +++ b/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; @@ -119,27 +120,58 @@ protected static ProcessResult RunCommandLineCompiler( additionalEnvironmentVars); } + /// + /// Setting ToolExe to "csc.exe" should use the built-in compiler regardless of apphost being used or not. + /// [Theory(Skip = "https://github.com/dotnet/roslyn/issues/80991"), CombinatorialData] - public void SdkBuild_Csc(bool useSharedCompilation) + [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2615118")] + public void SdkBuild_Csc(bool useSharedCompilation, bool overrideToolExe, bool useAppHost) { - var result = RunMsbuild( - "/v:n /m /nr:false /t:Build /restore Test.csproj", - _tempDirectory, - new Dictionary + if (!ManagedToolTask.IsBuiltinToolRunningOnCoreClr && !useAppHost) + { + _output.WriteLine("Skipping test case: netfx compiler always uses apphost."); + return; + } + + var originalAppHost = Path.Combine(ManagedToolTask.GetToolDirectory(), $"csc{PlatformInformation.ExeExtension}"); + var backupAppHost = originalAppHost + ".bak"; + if (!useAppHost) + { + _output.WriteLine($"Apphost: {originalAppHost}"); + File.Move(originalAppHost, backupAppHost); + } + + ProcessResult? result; + + try + { + result = RunMsbuild( + "/v:n /m /nr:false /t:Build /restore Test.csproj" + + (overrideToolExe ? $" /p:CscToolExe=csc{PlatformInformation.ExeExtension}" : ""), + _tempDirectory, + new Dictionary + { + { "File.cs", """ + class Program { static void Main() { System.Console.WriteLine("Hello from file"); } } + """ }, + { "Test.csproj", $""" + + + + netstandard2.0 + {useSharedCompilation} + + + """ }, + }); + } + finally + { + if (!useAppHost) { - { "File.cs", """ - class Program { static void Main() { System.Console.WriteLine("Hello from file"); } } - """ }, - { "Test.csproj", $""" - - - - netstandard2.0 - {useSharedCompilation} - - - """ }, - }); + File.Move(backupAppHost, originalAppHost); + } + } if (result == null) return; @@ -147,35 +179,74 @@ class Program { static void Main() { System.Console.WriteLine("Hello from file") Assert.Equal(0, result.ExitCode); Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output); - Assert.DoesNotContain("csc.dll", result.Output); - Assert.Contains(ExecutionConditionUtil.IsWindows ? "csc.exe" : "csc", result.Output); + + if (useAppHost) + { + Assert.DoesNotContain("csc.dll", result.Output); + Assert.Contains($"csc{PlatformInformation.ExeExtension} ", result.Output); + } + else + { + Assert.Contains("csc.dll", result.Output); + Assert.DoesNotContain($"csc{PlatformInformation.ExeExtension} ", result.Output); + } } + /// + /// Setting ToolExe to "vbc.exe" should use the built-in compiler regardless of apphost being used or not. + /// [Theory(Skip = "https://github.com/dotnet/roslyn/issues/80991"), CombinatorialData] - public void SdkBuild_Vbc(bool useSharedCompilation) + [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2615118")] + public void SdkBuild_Vbc(bool useSharedCompilation, bool overrideToolExe, bool useAppHost) { - var result = RunMsbuild( - "/v:n /m /nr:false /t:Build /restore Test.vbproj", - _tempDirectory, - new Dictionary + if (!ManagedToolTask.IsBuiltinToolRunningOnCoreClr && !useAppHost) + { + _output.WriteLine("Skipping test case: netfx compiler always uses apphost."); + return; + } + + var originalAppHost = Path.Combine(ManagedToolTask.GetToolDirectory(), $"vbc{PlatformInformation.ExeExtension}"); + var backupAppHost = originalAppHost + ".bak"; + if (!useAppHost) + { + File.Move(originalAppHost, backupAppHost); + } + + ProcessResult? result; + + try + { + result = RunMsbuild( + "/v:n /m /nr:false /t:Build /restore Test.vbproj" + + (overrideToolExe ? $" /p:VbcToolExe=vbc{PlatformInformation.ExeExtension}" : ""), + _tempDirectory, + new Dictionary + { + { "File.vb", """ + Public Module Program + Public Sub Main() + System.Console.WriteLine("Hello from file") + End Sub + End Module + """ }, + { "Test.vbproj", $""" + + + + netstandard2.0 + {useSharedCompilation} + + + """ }, + }); + } + finally + { + if (!useAppHost) { - { "File.vb", """ - Public Module Program - Public Sub Main() - System.Console.WriteLine("Hello from file") - End Sub - End Module - """ }, - { "Test.vbproj", $""" - - - - netstandard2.0 - {useSharedCompilation} - - - """ }, - }); + File.Move(backupAppHost, originalAppHost); + } + } if (result == null) return; @@ -183,8 +254,17 @@ End Module Assert.Equal(0, result.ExitCode); Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output); - Assert.DoesNotContain("vbc.dll", result.Output); - Assert.Contains(ExecutionConditionUtil.IsWindows ? "vbc.exe" : "vbc", result.Output); + + if (useAppHost) + { + Assert.DoesNotContain("vbc.dll", result.Output); + Assert.Contains($"vbc{PlatformInformation.ExeExtension} ", result.Output); + } + else + { + Assert.Contains("vbc.dll", result.Output); + Assert.DoesNotContain($"vbc{PlatformInformation.ExeExtension} ", result.Output); + } } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79907")] diff --git a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs index 8ec9053999c38..bbdf6dff60322 100644 --- a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.BuildTasks; using Microsoft.CodeAnalysis.BuildTasks.UnitTests.TestUtilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; @@ -434,6 +435,30 @@ public void DisableSdkPath() Assert.Equal(@"/optionstrict:custom /nosdkpath", vbc.GenerateResponseFileContents()); } + /// + /// Setting ToolExe to "vbc.exe" should use the built-in compiler regardless of apphost being used or not. + /// + [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2615118")] + public void BuiltInToolExe(bool useAppHost, bool setToolExe) + { + var vbc = new Vbc(); + vbc.UseAppHost_TestOnly = useAppHost; + if (setToolExe) + { + vbc.ToolExe = $"vbc{PlatformInformation.ExeExtension}"; + } + if (useAppHost) + { + AssertEx.Equal(vbc.PathToBuiltInTool, vbc.GeneratePathToTool()); + AssertEx.Equal("", vbc.GenerateCommandLineContents()); + } + else + { + AssertEx.Equal(RuntimeHostInfo.GetDotNetPathOrDefault(), vbc.GeneratePathToTool()); + AssertEx.Equal(RuntimeHostInfo.GetDotNetExecCommandLine(vbc.PathToBuiltInTool, ""), vbc.GenerateCommandLineContents()); + } + } + [Fact] public void EditorConfig() {