From 42b23923045a80c51e57af19216b209dcd64b632 Mon Sep 17 00:00:00 2001 From: AR-May <67507805+AR-May@users.noreply.github.com> Date: Tue, 31 May 2022 17:31:51 +0200 Subject: [PATCH 1/3] Fix msbuild server process launch. --- src/Build/BackEnd/Client/MSBuildClient.cs | 59 ++--- .../Components/Communications/NodeLauncher.cs | 216 ++++++++++++++++++ .../NodeProviderOutOfProcBase.cs | 203 +--------------- src/Build/Microsoft.Build.csproj | 1 + .../PublicAPI/net/PublicAPI.Unshipped.txt | 2 +- .../netstandard/PublicAPI.Unshipped.txt | 2 +- src/MSBuild/MSBuildClientApp.cs | 37 +-- 7 files changed, 246 insertions(+), 274 deletions(-) create mode 100644 src/Build/BackEnd/Components/Communications/NodeLauncher.cs diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index c8b3c4adc7f..4233e1ddffe 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -8,7 +8,6 @@ using System.Globalization; using System.IO; using System.IO.Pipes; -using System.Runtime.InteropServices; using System.Threading; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Client; @@ -32,14 +31,10 @@ public sealed class MSBuildClient private readonly Dictionary _serverEnvironmentVariables; /// - /// Location of executable file to launch the server process. That should be either dotnet.exe or MSBuild.exe location. + /// Full path to current MSBuild.exe if executable is MSBuild.exe, + /// or to version of MSBuild.dll found to be associated with the current process. /// - private readonly string _exeLocation; - - /// - /// Location of dll file to launch the server process if needed. Empty if executable is msbuild.exe and not empty if dotnet.exe. - /// - private readonly string _dllLocation; + private readonly string _msbuildLocation; /// /// The MSBuild client execution result. @@ -85,18 +80,15 @@ public sealed class MSBuildClient /// /// Public constructor with parameters. /// - /// Location of executable file to launch the server process. - /// That should be either dotnet.exe or MSBuild.exe location. - /// Location of dll file to launch the server process if needed. - /// Empty if executable is msbuild.exe and not empty if dotnet.exe. - public MSBuildClient(string exeLocation, string dllLocation) + /// Full path to current MSBuild.exe if executable is MSBuild.exe, + /// or to version of MSBuild.dll found to be associated with the current process. + public MSBuildClient(string msbuildLocation) { _serverEnvironmentVariables = new(); _exitResult = new(); // dll & exe locations - _exeLocation = exeLocation; - _dllLocation = dllLocation; + _msbuildLocation = msbuildLocation; // Client <-> Server communication stream _handshake = GetHandshake(); @@ -257,15 +249,20 @@ private bool TryLaunchServer() } string[] msBuildServerOptions = new string[] { - _dllLocation, "/nologo", "/nodemode:8" }; + string? useMSBuildServerEnvVarValue = Environment.GetEnvironmentVariable(Traits.UseMSBuildServerEnvVarName); try { - Process msbuildProcess = LaunchNode(_exeLocation, string.Join(" ", msBuildServerOptions), _serverEnvironmentVariables); - CommunicationsUtilities.Trace("Server is launched with PID: {0}", msbuildProcess.Id); + // Disable MSBuild server for a child process, preventing an infinite recurson. + Environment.SetEnvironmentVariable(Traits.UseMSBuildServerEnvVarName, ""); + + NodeLauncher nodeLauncher = new NodeLauncher(); + CommunicationsUtilities.Trace("Starting Server..."); + Process msbuildProcess = nodeLauncher.Start(_msbuildLocation, string.Join(" ", msBuildServerOptions)); + CommunicationsUtilities.Trace("Server started with PID: {0}", msbuildProcess?.Id); } catch (Exception ex) { @@ -273,32 +270,12 @@ private bool TryLaunchServer() _exitResult.MSBuildClientExitType = MSBuildClientExitType.LaunchError; return false; } - - return true; - } - - private Process LaunchNode(string exeLocation, string msBuildServerArguments, Dictionary serverEnvironmentVariables) - { - CommunicationsUtilities.Trace("Launching server node from {0} with arguments {1}", exeLocation, msBuildServerArguments); - ProcessStartInfo processStartInfo = new() - { - FileName = exeLocation, - Arguments = msBuildServerArguments, - UseShellExecute = false - }; - - foreach (var entry in serverEnvironmentVariables) + finally { - processStartInfo.Environment[entry.Key] = entry.Value; + Environment.SetEnvironmentVariable(Traits.UseMSBuildServerEnvVarName, useMSBuildServerEnvVarValue); } - // We remove env to enable MSBuild Server that might be equal to 1, so we do not get an infinite recursion here. - processStartInfo.Environment[Traits.UseMSBuildServerEnvVarName] = "0"; - - processStartInfo.CreateNoWindow = true; - processStartInfo.UseShellExecute = false; - - return Process.Start(processStartInfo) ?? throw new InvalidOperationException("MSBuild server node failed to launch."); + return true; } private bool TrySendBuildCommand(string commandLine) diff --git a/src/Build/BackEnd/Components/Communications/NodeLauncher.cs b/src/Build/BackEnd/Components/Communications/NodeLauncher.cs new file mode 100644 index 00000000000..d9ec5ac0657 --- /dev/null +++ b/src/Build/BackEnd/Components/Communications/NodeLauncher.cs @@ -0,0 +1,216 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Build.Exceptions; +using Microsoft.Build.Framework; +using Microsoft.Build.Internal; +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; +using BackendNativeMethods = Microsoft.Build.BackEnd.NativeMethods; + +#nullable disable + +namespace Microsoft.Build.BackEnd +{ + internal class NodeLauncher + { + +#if RUNTIME_TYPE_NETCORE || MONO + public static string CurrentHost; +#endif + + /// + /// Identify the .NET host of the current process. + /// + /// The full path to the executable hosting the current process, or null if running on Full Framework on Windows. + public static string GetCurrentHost() + { +#if RUNTIME_TYPE_NETCORE || MONO + if (CurrentHost == null) + { + string dotnetExe = Path.Combine(FileUtilities.GetFolderAbove(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, 2), + NativeMethodsShared.IsWindows ? "dotnet.exe" : "dotnet"); + if (File.Exists(dotnetExe)) + { + CurrentHost = dotnetExe; + } + else + { + using (Process currentProcess = Process.GetCurrentProcess()) + { + CurrentHost = currentProcess.MainModule.FileName; + } + } + } + + return CurrentHost; +#else + return null; +#endif + } + + /// + /// Creates a new MSBuild process + /// + public Process Start(string msbuildLocation, string commandLineArgs) + { + // Should always have been set already. + ErrorUtilities.VerifyThrowInternalLength(msbuildLocation, nameof(msbuildLocation)); + + if (!FileSystems.Default.FileExists(msbuildLocation)) + { + throw new BuildAbortedException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("CouldNotFindMSBuildExe", msbuildLocation)); + } + + // Repeat the executable name as the first token of the command line because the command line + // parser logic expects it and will otherwise skip the first argument + commandLineArgs = $"\"{msbuildLocation}\" {commandLineArgs}"; + + BackendNativeMethods.STARTUP_INFO startInfo = new(); + startInfo.cb = Marshal.SizeOf(); + + // Null out the process handles so that the parent process does not wait for the child process + // to exit before it can exit. + uint creationFlags = 0; + if (Traits.Instance.EscapeHatches.EnsureStdOutForChildNodesIsPrimaryStdout) + { + creationFlags = BackendNativeMethods.NORMALPRIORITYCLASS; + } + + if (String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDNODEWINDOW"))) + { + if (!Traits.Instance.EscapeHatches.EnsureStdOutForChildNodesIsPrimaryStdout) + { + // Redirect the streams of worker nodes so that this MSBuild.exe's + // parent doesn't wait on idle worker nodes to close streams + // after the build is complete. + startInfo.hStdError = BackendNativeMethods.InvalidHandle; + startInfo.hStdInput = BackendNativeMethods.InvalidHandle; + startInfo.hStdOutput = BackendNativeMethods.InvalidHandle; + startInfo.dwFlags = BackendNativeMethods.STARTFUSESTDHANDLES; + creationFlags |= BackendNativeMethods.CREATENOWINDOW; + } + } + else + { + creationFlags |= BackendNativeMethods.CREATE_NEW_CONSOLE; + } + + CommunicationsUtilities.Trace("Launching node from {0}", msbuildLocation); + + string exeName = msbuildLocation; + +#if RUNTIME_TYPE_NETCORE || MONO + // Mono automagically uses the current mono, to execute a managed assembly + if (!NativeMethodsShared.IsMono) + { + // Run the child process with the same host as the currently-running process. + exeName = GetCurrentHost(); + } +#endif + + if (!NativeMethodsShared.IsWindows) + { + ProcessStartInfo processStartInfo = new ProcessStartInfo(); + processStartInfo.FileName = exeName; + processStartInfo.Arguments = commandLineArgs; + if (!Traits.Instance.EscapeHatches.EnsureStdOutForChildNodesIsPrimaryStdout) + { + // Redirect the streams of worker nodes so that this MSBuild.exe's + // parent doesn't wait on idle worker nodes to close streams + // after the build is complete. + processStartInfo.RedirectStandardInput = true; + processStartInfo.RedirectStandardOutput = true; + processStartInfo.RedirectStandardError = true; + processStartInfo.CreateNoWindow = (creationFlags | BackendNativeMethods.CREATENOWINDOW) == BackendNativeMethods.CREATENOWINDOW; + } + processStartInfo.UseShellExecute = false; + + Process process; + try + { + process = Process.Start(processStartInfo); + } + catch (Exception ex) + { + CommunicationsUtilities.Trace + ( + "Failed to launch node from {0}. CommandLine: {1}" + Environment.NewLine + "{2}", + msbuildLocation, + commandLineArgs, + ex.ToString() + ); + + throw new NodeFailedToLaunchException(ex); + } + + CommunicationsUtilities.Trace("Successfully launched {1} node with PID {0}", process.Id, exeName); + return process; + } + else + { +#if RUNTIME_TYPE_NETCORE + // Repeat the executable name in the args to suit CreateProcess + commandLineArgs = $"\"{exeName}\" {commandLineArgs}"; +#endif + + BackendNativeMethods.PROCESS_INFORMATION processInfo = new(); + BackendNativeMethods.SECURITY_ATTRIBUTES processSecurityAttributes = new(); + BackendNativeMethods.SECURITY_ATTRIBUTES threadSecurityAttributes = new(); + processSecurityAttributes.nLength = Marshal.SizeOf(); + threadSecurityAttributes.nLength = Marshal.SizeOf(); + + bool result = BackendNativeMethods.CreateProcess + ( + exeName, + commandLineArgs, + ref processSecurityAttributes, + ref threadSecurityAttributes, + false, + creationFlags, + BackendNativeMethods.NullPtr, + null, + ref startInfo, + out processInfo + ); + + if (!result) + { + // Creating an instance of this exception calls GetLastWin32Error and also converts it to a user-friendly string. + System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(); + + CommunicationsUtilities.Trace + ( + "Failed to launch node from {0}. System32 Error code {1}. Description {2}. CommandLine: {2}", + msbuildLocation, + e.NativeErrorCode.ToString(CultureInfo.InvariantCulture), + e.Message, + commandLineArgs + ); + + throw new NodeFailedToLaunchException(e.NativeErrorCode.ToString(CultureInfo.InvariantCulture), e.Message); + } + + int childProcessId = processInfo.dwProcessId; + + if (processInfo.hProcess != IntPtr.Zero && processInfo.hProcess != NativeMethods.InvalidHandle) + { + NativeMethodsShared.CloseHandle(processInfo.hProcess); + } + + if (processInfo.hThread != IntPtr.Zero && processInfo.hThread != NativeMethods.InvalidHandle) + { + NativeMethodsShared.CloseHandle(processInfo.hThread); + } + + CommunicationsUtilities.Trace("Successfully launched {1} node with PID {0}", childProcessId, exeName); + return Process.GetProcessById(childProcessId); + } + } + } +} diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index b667e7a60f2..df45d946165 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -19,13 +19,8 @@ #if FEATURE_APM using Microsoft.Build.Eventing; #endif -using Microsoft.Build.Exceptions; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -using Microsoft.Build.Shared.FileSystem; -using Microsoft.Build.Utilities; - -using BackendNativeMethods = Microsoft.Build.BackEnd.NativeMethods; using Task = System.Threading.Tasks.Task; using Microsoft.Build.Framework; using Microsoft.Build.BackEnd.Logging; @@ -274,7 +269,8 @@ protected NodeContext GetNode(string msbuildLocation, string commandLineArgs, in #endif // Create the node process - Process msbuildProcess = LaunchNode(msbuildLocation, commandLineArgs); + NodeLauncher nodeLauncher = new NodeLauncher(); + Process msbuildProcess = nodeLauncher.Start(msbuildLocation, commandLineArgs); _processesToIgnore.Add(GetProcessesToIgnoreKey(hostHandshake, msbuildProcess.Id)); // Note, when running under IMAGEFILEEXECUTIONOPTIONS registry key to debug, the process ID @@ -332,7 +328,7 @@ protected NodeContext GetNode(string msbuildLocation, string commandLineArgs, in msbuildLocation = "MSBuild.exe"; } - var expectedProcessName = Path.GetFileNameWithoutExtension(GetCurrentHost() ?? msbuildLocation); + var expectedProcessName = Path.GetFileNameWithoutExtension(NodeLauncher.GetCurrentHost() ?? msbuildLocation); List nodeProcesses = new List(Process.GetProcessesByName(expectedProcessName)); @@ -441,199 +437,6 @@ private Stream TryConnectToProcess(int nodeProcessId, int timeout, Handshake han return null; } - /// - /// Creates a new MSBuild process - /// - private Process LaunchNode(string msbuildLocation, string commandLineArgs) - { - // Should always have been set already. - ErrorUtilities.VerifyThrowInternalLength(msbuildLocation, nameof(msbuildLocation)); - - if (!FileSystems.Default.FileExists(msbuildLocation)) - { - throw new BuildAbortedException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("CouldNotFindMSBuildExe", msbuildLocation)); - } - - // Repeat the executable name as the first token of the command line because the command line - // parser logic expects it and will otherwise skip the first argument - commandLineArgs = $"\"{msbuildLocation}\" {commandLineArgs}"; - - BackendNativeMethods.STARTUP_INFO startInfo = new(); - startInfo.cb = Marshal.SizeOf(); - - // Null out the process handles so that the parent process does not wait for the child process - // to exit before it can exit. - uint creationFlags = 0; - if (Traits.Instance.EscapeHatches.EnsureStdOutForChildNodesIsPrimaryStdout) - { - creationFlags = BackendNativeMethods.NORMALPRIORITYCLASS; - } - - if (String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDNODEWINDOW"))) - { - if (!Traits.Instance.EscapeHatches.EnsureStdOutForChildNodesIsPrimaryStdout) - { - // Redirect the streams of worker nodes so that this MSBuild.exe's - // parent doesn't wait on idle worker nodes to close streams - // after the build is complete. - startInfo.hStdError = BackendNativeMethods.InvalidHandle; - startInfo.hStdInput = BackendNativeMethods.InvalidHandle; - startInfo.hStdOutput = BackendNativeMethods.InvalidHandle; - startInfo.dwFlags = BackendNativeMethods.STARTFUSESTDHANDLES; - creationFlags |= BackendNativeMethods.CREATENOWINDOW; - } - } - else - { - creationFlags |= BackendNativeMethods.CREATE_NEW_CONSOLE; - } - - CommunicationsUtilities.Trace("Launching node from {0}", msbuildLocation); - - string exeName = msbuildLocation; - -#if RUNTIME_TYPE_NETCORE || MONO - // Mono automagically uses the current mono, to execute a managed assembly - if (!NativeMethodsShared.IsMono) - { - // Run the child process with the same host as the currently-running process. - exeName = GetCurrentHost(); - } -#endif - - if (!NativeMethodsShared.IsWindows) - { - ProcessStartInfo processStartInfo = new ProcessStartInfo(); - processStartInfo.FileName = exeName; - processStartInfo.Arguments = commandLineArgs; - if (!Traits.Instance.EscapeHatches.EnsureStdOutForChildNodesIsPrimaryStdout) - { - // Redirect the streams of worker nodes so that this MSBuild.exe's - // parent doesn't wait on idle worker nodes to close streams - // after the build is complete. - processStartInfo.RedirectStandardInput = true; - processStartInfo.RedirectStandardOutput = true; - processStartInfo.RedirectStandardError = true; - processStartInfo.CreateNoWindow = (creationFlags | BackendNativeMethods.CREATENOWINDOW) == BackendNativeMethods.CREATENOWINDOW; - } - processStartInfo.UseShellExecute = false; - - Process process; - try - { - process = Process.Start(processStartInfo); - } - catch (Exception ex) - { - CommunicationsUtilities.Trace - ( - "Failed to launch node from {0}. CommandLine: {1}" + Environment.NewLine + "{2}", - msbuildLocation, - commandLineArgs, - ex.ToString() - ); - - throw new NodeFailedToLaunchException(ex); - } - - CommunicationsUtilities.Trace("Successfully launched {1} node with PID {0}", process.Id, exeName); - return process; - } - else - { -#if RUNTIME_TYPE_NETCORE - // Repeat the executable name in the args to suit CreateProcess - commandLineArgs = $"\"{exeName}\" {commandLineArgs}"; -#endif - - BackendNativeMethods.PROCESS_INFORMATION processInfo = new(); - BackendNativeMethods.SECURITY_ATTRIBUTES processSecurityAttributes = new(); - BackendNativeMethods.SECURITY_ATTRIBUTES threadSecurityAttributes = new(); - processSecurityAttributes.nLength = Marshal.SizeOf(); - threadSecurityAttributes.nLength = Marshal.SizeOf(); - - bool result = BackendNativeMethods.CreateProcess - ( - exeName, - commandLineArgs, - ref processSecurityAttributes, - ref threadSecurityAttributes, - false, - creationFlags, - BackendNativeMethods.NullPtr, - null, - ref startInfo, - out processInfo - ); - - if (!result) - { - // Creating an instance of this exception calls GetLastWin32Error and also converts it to a user-friendly string. - System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(); - - CommunicationsUtilities.Trace - ( - "Failed to launch node from {0}. System32 Error code {1}. Description {2}. CommandLine: {2}", - msbuildLocation, - e.NativeErrorCode.ToString(CultureInfo.InvariantCulture), - e.Message, - commandLineArgs - ); - - throw new NodeFailedToLaunchException(e.NativeErrorCode.ToString(CultureInfo.InvariantCulture), e.Message); - } - - int childProcessId = processInfo.dwProcessId; - - if (processInfo.hProcess != IntPtr.Zero && processInfo.hProcess != NativeMethods.InvalidHandle) - { - NativeMethodsShared.CloseHandle(processInfo.hProcess); - } - - if (processInfo.hThread != IntPtr.Zero && processInfo.hThread != NativeMethods.InvalidHandle) - { - NativeMethodsShared.CloseHandle(processInfo.hThread); - } - - CommunicationsUtilities.Trace("Successfully launched {1} node with PID {0}", childProcessId, exeName); - return Process.GetProcessById(childProcessId); - } - } - -#if RUNTIME_TYPE_NETCORE || MONO - private static string CurrentHost; -#endif - - /// - /// Identify the .NET host of the current process. - /// - /// The full path to the executable hosting the current process, or null if running on Full Framework on Windows. - private static string GetCurrentHost() - { -#if RUNTIME_TYPE_NETCORE || MONO - if (CurrentHost == null) - { - string dotnetExe = Path.Combine(FileUtilities.GetFolderAbove(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, 2), - NativeMethodsShared.IsWindows ? "dotnet.exe" : "dotnet"); - if (File.Exists(dotnetExe)) - { - CurrentHost = dotnetExe; - } - else - { - using (Process currentProcess = Process.GetCurrentProcess()) - { - CurrentHost = currentProcess.MainModule.FileName; - } - } - } - - return CurrentHost; -#else - return null; -#endif - } - /// /// Class which wraps up the communications infrastructure for a given node. /// diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 9c4bbac8ee4..aacd09479bf 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -350,6 +350,7 @@ + diff --git a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt index ee20877adfb..a39e6b88d8c 100644 --- a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt @@ -1,7 +1,7 @@ Microsoft.Build.Evaluation.ProjectCollection.ProjectCollection(System.Collections.Generic.IDictionary globalProperties, System.Collections.Generic.IEnumerable loggers, System.Collections.Generic.IEnumerable remoteLoggers, Microsoft.Build.Evaluation.ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly, bool reuseProjectRootElementCache) -> void Microsoft.Build.Execution.MSBuildClient Microsoft.Build.Execution.MSBuildClient.Execute(string commandLine, System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Execution.MSBuildClientExitResult -Microsoft.Build.Execution.MSBuildClient.MSBuildClient(string exeLocation, string dllLocation) -> void +Microsoft.Build.Execution.MSBuildClient.MSBuildClient(string msbuildLocation) -> void Microsoft.Build.Execution.MSBuildClientExitResult Microsoft.Build.Execution.MSBuildClientExitResult.MSBuildAppExitTypeString.get -> string Microsoft.Build.Execution.MSBuildClientExitResult.MSBuildAppExitTypeString.set -> void diff --git a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt index 44179d2f0e1..1019cb1d919 100644 --- a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -1,7 +1,7 @@ Microsoft.Build.Evaluation.ProjectCollection.ProjectCollection(System.Collections.Generic.IDictionary globalProperties, System.Collections.Generic.IEnumerable loggers, System.Collections.Generic.IEnumerable remoteLoggers, Microsoft.Build.Evaluation.ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly, bool reuseProjectRootElementCache) -> void Microsoft.Build.Execution.MSBuildClient Microsoft.Build.Execution.MSBuildClient.Execute(string commandLine, System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Execution.MSBuildClientExitResult -Microsoft.Build.Execution.MSBuildClient.MSBuildClient(string exeLocation, string dllLocation) -> void +Microsoft.Build.Execution.MSBuildClient.MSBuildClient(string msbuildLocation) -> void Microsoft.Build.Execution.MSBuildClientExitResult Microsoft.Build.Execution.MSBuildClientExitResult.MSBuildAppExitTypeString.get -> string Microsoft.Build.Execution.MSBuildClientExitResult.MSBuildAppExitTypeString.set -> void diff --git a/src/MSBuild/MSBuildClientApp.cs b/src/MSBuild/MSBuildClientApp.cs index de83c3a10e3..ef39eb8f1b3 100644 --- a/src/MSBuild/MSBuildClientApp.cs +++ b/src/MSBuild/MSBuildClientApp.cs @@ -42,34 +42,12 @@ public static MSBuildApp.ExitType Execute( CancellationToken cancellationToken ) { - string? exeLocation; - string? dllLocation; - -#if RUNTIME_TYPE_NETCORE || MONO - // Run the child process with the same host as the currently-running process. - // Mono automatically uses the current mono, to execute a managed assembly. - if (!NativeMethodsShared.IsMono) - { - // _exeFileLocation consists the msbuild dll instead. - dllLocation = BuildEnvironmentHelper.Instance.CurrentMSBuildExePath; - exeLocation = GetCurrentHost(); - } - else - { - // _exeFileLocation consists the msbuild dll instead. - exeLocation = BuildEnvironmentHelper.Instance.CurrentMSBuildExePath; - dllLocation = String.Empty; - } -#else - exeLocation = BuildEnvironmentHelper.Instance.CurrentMSBuildExePath; - dllLocation = String.Empty; -#endif + string msbuildLocation = BuildEnvironmentHelper.Instance.CurrentMSBuildExePath; return Execute( commandLine, cancellationToken, - exeLocation, - dllLocation + msbuildLocation ); } @@ -80,10 +58,8 @@ CancellationToken cancellationToken /// on the command line is assumed to be the name/path of the executable, and /// is ignored. /// Cancellation token. - /// Location of executable file to launch the server process. - /// That should be either dotnet.exe or MSBuild.exe location. - /// Location of dll file to launch the server process if needed. - /// Empty if executable is msbuild.exe and not empty if dotnet.exe. + /// Full path to current MSBuild.exe if executable is MSBuild.exe, + /// or to version of MSBuild.dll found to be associated with the current process. /// A value of type that indicates whether the build succeeded, /// or the manner in which it failed. public static MSBuildApp.ExitType Execute( @@ -93,8 +69,7 @@ public static MSBuildApp.ExitType Execute( string[] commandLine, #endif CancellationToken cancellationToken, - string exeLocation, - string dllLocation + string msbuildLocation ) { // MSBuild client orchestration. @@ -103,7 +78,7 @@ string dllLocation #else string commandLineString = commandLine; #endif - MSBuildClient msbuildClient = new MSBuildClient(exeLocation, dllLocation); + MSBuildClient msbuildClient = new MSBuildClient(msbuildLocation); MSBuildClientExitResult exitResult = msbuildClient.Execute(commandLineString, cancellationToken); if (exitResult.MSBuildClientExitType == MSBuildClientExitType.ServerBusy || From 54957afcddebc6e308b67cef702d5d31306863d5 Mon Sep 17 00:00:00 2001 From: AR-May <67507805+AR-May@users.noreply.github.com> Date: Sun, 5 Jun 2022 21:09:43 +0200 Subject: [PATCH 2/3] Move GetCurrentHost to its own class. --- .../Components/Communications/CurrentHost.cs | 49 +++++++++++++++++++ .../Components/Communications/NodeLauncher.cs | 37 +------------- .../NodeProviderOutOfProcBase.cs | 2 +- src/Build/Microsoft.Build.csproj | 3 +- 4 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 src/Build/BackEnd/Components/Communications/CurrentHost.cs diff --git a/src/Build/BackEnd/Components/Communications/CurrentHost.cs b/src/Build/BackEnd/Components/Communications/CurrentHost.cs new file mode 100644 index 00000000000..3b27142407f --- /dev/null +++ b/src/Build/BackEnd/Components/Communications/CurrentHost.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; +using System.IO; +using Microsoft.Build.Shared; + +#nullable disable + +namespace Microsoft.Build.BackEnd +{ + internal static class CurrentHost + { + +#if RUNTIME_TYPE_NETCORE || MONO + private static string _currentHost; +#endif + + /// + /// Identify the .NET host of the current process. + /// + /// The full path to the executable hosting the current process, or null if running on Full Framework on Windows. + public static string GetCurrentHost() + { +#if RUNTIME_TYPE_NETCORE || MONO + if (_currentHost == null) + { + string dotnetExe = Path.Combine(FileUtilities.GetFolderAbove(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, 2), + NativeMethodsShared.IsWindows ? "dotnet.exe" : "dotnet"); + if (File.Exists(dotnetExe)) + { + _currentHost = dotnetExe; + } + else + { + using (Process currentProcess = Process.GetCurrentProcess()) + { + _currentHost = currentProcess.MainModule.FileName; + } + } + } + + return _currentHost; +#else + return null; +#endif + } + } +} diff --git a/src/Build/BackEnd/Components/Communications/NodeLauncher.cs b/src/Build/BackEnd/Components/Communications/NodeLauncher.cs index d9ec5ac0657..652f7dda74c 100644 --- a/src/Build/BackEnd/Components/Communications/NodeLauncher.cs +++ b/src/Build/BackEnd/Components/Communications/NodeLauncher.cs @@ -19,41 +19,6 @@ namespace Microsoft.Build.BackEnd { internal class NodeLauncher { - -#if RUNTIME_TYPE_NETCORE || MONO - public static string CurrentHost; -#endif - - /// - /// Identify the .NET host of the current process. - /// - /// The full path to the executable hosting the current process, or null if running on Full Framework on Windows. - public static string GetCurrentHost() - { -#if RUNTIME_TYPE_NETCORE || MONO - if (CurrentHost == null) - { - string dotnetExe = Path.Combine(FileUtilities.GetFolderAbove(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, 2), - NativeMethodsShared.IsWindows ? "dotnet.exe" : "dotnet"); - if (File.Exists(dotnetExe)) - { - CurrentHost = dotnetExe; - } - else - { - using (Process currentProcess = Process.GetCurrentProcess()) - { - CurrentHost = currentProcess.MainModule.FileName; - } - } - } - - return CurrentHost; -#else - return null; -#endif - } - /// /// Creates a new MSBuild process /// @@ -110,7 +75,7 @@ public Process Start(string msbuildLocation, string commandLineArgs) if (!NativeMethodsShared.IsMono) { // Run the child process with the same host as the currently-running process. - exeName = GetCurrentHost(); + exeName = CurrentHost.GetCurrentHost(); } #endif diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index df45d946165..0beb610c0aa 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -328,7 +328,7 @@ protected NodeContext GetNode(string msbuildLocation, string commandLineArgs, in msbuildLocation = "MSBuild.exe"; } - var expectedProcessName = Path.GetFileNameWithoutExtension(NodeLauncher.GetCurrentHost() ?? msbuildLocation); + var expectedProcessName = Path.GetFileNameWithoutExtension(CurrentHost.GetCurrentHost() ?? msbuildLocation); List nodeProcesses = new List(Process.GetProcessesByName(expectedProcessName)); diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index aacd09479bf..f6c13b14634 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -1,4 +1,4 @@ - + @@ -149,6 +149,7 @@ + From bfbfbf9e9e13b91047f1c6ab2dd47464aad75b83 Mon Sep 17 00:00:00 2001 From: AR-May <67507805+AR-May@users.noreply.github.com> Date: Sun, 5 Jun 2022 21:16:05 +0200 Subject: [PATCH 3/3] fix name. --- .../BackEnd/Components/Communications/CurrentHost.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Build/BackEnd/Components/Communications/CurrentHost.cs b/src/Build/BackEnd/Components/Communications/CurrentHost.cs index 3b27142407f..81116ecb054 100644 --- a/src/Build/BackEnd/Components/Communications/CurrentHost.cs +++ b/src/Build/BackEnd/Components/Communications/CurrentHost.cs @@ -13,7 +13,7 @@ internal static class CurrentHost { #if RUNTIME_TYPE_NETCORE || MONO - private static string _currentHost; + private static string s_currentHost; #endif /// @@ -23,24 +23,24 @@ internal static class CurrentHost public static string GetCurrentHost() { #if RUNTIME_TYPE_NETCORE || MONO - if (_currentHost == null) + if (s_currentHost == null) { string dotnetExe = Path.Combine(FileUtilities.GetFolderAbove(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, 2), NativeMethodsShared.IsWindows ? "dotnet.exe" : "dotnet"); if (File.Exists(dotnetExe)) { - _currentHost = dotnetExe; + s_currentHost = dotnetExe; } else { using (Process currentProcess = Process.GetCurrentProcess()) { - _currentHost = currentProcess.MainModule.FileName; + s_currentHost = currentProcess.MainModule.FileName; } } } - return _currentHost; + return s_currentHost; #else return null; #endif