diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index fad0f82acea..f6d36d71c8a 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -37,6 +37,16 @@ public sealed class MSBuildClient /// private readonly string _msbuildLocation; + /// + /// The command line to process. + /// The first argument on the command line is assumed to be the name/path of the executable, and is ignored. + /// +#if FEATURE_GET_COMMANDLINE + private readonly string _commandLine; +#else + private readonly string[] _commandLine; +#endif + /// /// The MSBuild client execution result. /// @@ -81,14 +91,23 @@ public sealed class MSBuildClient /// /// Public constructor with parameters. /// + /// The command line to process. The first argument + /// on the command line is assumed to be the name/path of the executable, and is ignored /// 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) + public MSBuildClient( +#if FEATURE_GET_COMMANDLINE + string commandLine, +#else + string[] commandLine, +#endif + string msbuildLocation) { _serverEnvironmentVariables = new(); _exitResult = new(); // dll & exe locations + _commandLine = commandLine; _msbuildLocation = msbuildLocation; // Client <-> Server communication stream @@ -108,15 +127,20 @@ public MSBuildClient(string msbuildLocation) /// Orchestrates the execution of the build on the server, /// responsible for client-server communication. /// - /// The command line to process. The first argument - /// on the command line is assumed to be the name/path of the executable, and - /// is ignored. /// Cancellation token. /// A value of type that indicates whether the build succeeded, /// or the manner in which it failed. - public MSBuildClientExitResult Execute(string commandLine, CancellationToken cancellationToken) + public MSBuildClientExitResult Execute(CancellationToken cancellationToken) { - CommunicationsUtilities.Trace("Executing build with command line '{0}'", commandLine); + // Command line in one string used only in human readable content. + string descriptiveCommandLine = +#if FEATURE_GET_COMMANDLINE + _commandLine; +#else + string.Join(" ", _commandLine); +#endif + + CommunicationsUtilities.Trace("Executing build with command line '{0}'", descriptiveCommandLine); string serverRunningMutexName = OutOfProcServerNode.GetRunningServerMutexName(_handshake); string serverBusyMutexName = OutOfProcServerNode.GetBusyServerMutexName(_handshake); @@ -150,8 +174,8 @@ public MSBuildClientExitResult Execute(string commandLine, CancellationToken can // Send build command. // Let's send it outside the packet pump so that we easier and quicker deal with possible issues with connection to server. - MSBuildEventSource.Log.MSBuildServerBuildStart(commandLine); - if (!TrySendBuildCommand(commandLine)) + MSBuildEventSource.Log.MSBuildServerBuildStart(descriptiveCommandLine); + if (!TrySendBuildCommand()) { CommunicationsUtilities.Trace("Failure to connect to a server."); _exitResult.MSBuildClientExitType = MSBuildClientExitType.ConnectionError; @@ -218,7 +242,7 @@ public MSBuildClientExitResult Execute(string commandLine, CancellationToken can _exitResult.MSBuildClientExitType = MSBuildClientExitType.Unexpected; } - MSBuildEventSource.Log.MSBuildServerBuildStop(commandLine, _numConsoleWritePackets, _sizeOfConsoleWritePackets, _exitResult.MSBuildClientExitType.ToString(), _exitResult.MSBuildAppExitTypeString); + MSBuildEventSource.Log.MSBuildServerBuildStop(descriptiveCommandLine, _numConsoleWritePackets, _sizeOfConsoleWritePackets, _exitResult.MSBuildClientExitType.ToString(), _exitResult.MSBuildAppExitTypeString); CommunicationsUtilities.Trace("Build finished."); return _exitResult; } @@ -298,11 +322,11 @@ private bool TryLaunchServer() return true; } - private bool TrySendBuildCommand(string commandLine) => TrySendPacket(() => GetServerNodeBuildCommand(commandLine)); + private bool TrySendBuildCommand() => TrySendPacket(() => GetServerNodeBuildCommand()); private bool TrySendCancelCommand() => TrySendPacket(() => new ServerNodeBuildCancel()); - private ServerNodeBuildCommand GetServerNodeBuildCommand(string commandLine) + private ServerNodeBuildCommand GetServerNodeBuildCommand() { Dictionary envVars = new(); @@ -320,7 +344,7 @@ private ServerNodeBuildCommand GetServerNodeBuildCommand(string commandLine) envVars[Traits.UseMSBuildServerEnvVarName] = "0"; return new ServerNodeBuildCommand( - commandLine, + _commandLine, startupDirectory: Directory.GetCurrentDirectory(), buildProcessEnvironment: envVars, CultureInfo.CurrentCulture, diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index 531ece6f3f1..55623f97c6e 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -19,7 +19,17 @@ namespace Microsoft.Build.Experimental /// public sealed class OutOfProcServerNode : INode, INodePacketFactory, INodePacketHandler { - private readonly Func _buildFunction; + /// + /// A callback used to execute command line build. + /// + public delegate (int exitCode, string exitType) BuildCallback( +#if FEATURE_GET_COMMANDLINE + string commandLine); +#else + string[] commandLine); +#endif + + private readonly BuildCallback _buildFunction; /// /// The endpoint used to talk to the host. @@ -63,7 +73,7 @@ public sealed class OutOfProcServerNode : INode, INodePacketFactory, INodePacket private string _serverBusyMutexName = default!; - public OutOfProcServerNode(Func buildFunction) + public OutOfProcServerNode(BuildCallback buildFunction) { _buildFunction = buildFunction; new Dictionary(); diff --git a/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs b/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs index 48ab050cf1e..f8ebf9b371d 100644 --- a/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs +++ b/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs @@ -13,7 +13,11 @@ namespace Microsoft.Build.BackEnd /// internal sealed class ServerNodeBuildCommand : INodePacket { +#if FEATURE_GET_COMMANDLINE private string _commandLine = default!; +#else + private string[] _commandLine = default!; +#endif private string _startupDirectory = default!; private Dictionary _buildProcessEnvironment = default!; private CultureInfo _culture = default!; @@ -25,9 +29,13 @@ internal sealed class ServerNodeBuildCommand : INodePacket public NodePacketType Type => NodePacketType.ServerNodeBuildCommand; /// - /// The startup directory + /// Command line including arguments /// +#if FEATURE_GET_COMMANDLINE public string CommandLine => _commandLine; +#else + public string[] CommandLine => _commandLine; +#endif /// /// The startup directory @@ -56,7 +64,15 @@ private ServerNodeBuildCommand() { } - public ServerNodeBuildCommand(string commandLine, string startupDirectory, Dictionary buildProcessEnvironment, CultureInfo culture, CultureInfo uiCulture) + public ServerNodeBuildCommand( +#if FEATURE_GET_COMMANDLINE + string commandLine, +#else + string[] commandLine, +#endif + string startupDirectory, + Dictionary buildProcessEnvironment, + CultureInfo culture, CultureInfo uiCulture) { _commandLine = commandLine; _startupDirectory = startupDirectory; diff --git a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt index da542899bb9..c4de070516f 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.Experimental.MSBuildClient -Microsoft.Build.Experimental.MSBuildClient.Execute(string commandLine, System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Experimental.MSBuildClientExitResult -Microsoft.Build.Experimental.MSBuildClient.MSBuildClient(string msbuildLocation) -> void +Microsoft.Build.Experimental.MSBuildClient.Execute(System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Experimental.MSBuildClientExitResult +Microsoft.Build.Experimental.MSBuildClient.MSBuildClient(string commandLine, string msbuildLocation) -> void Microsoft.Build.Experimental.MSBuildClientExitResult Microsoft.Build.Experimental.MSBuildClientExitResult.MSBuildAppExitTypeString.get -> string Microsoft.Build.Experimental.MSBuildClientExitResult.MSBuildAppExitTypeString.set -> void @@ -15,5 +15,6 @@ Microsoft.Build.Experimental.MSBuildClientExitType.ServerBusy = 1 -> Microsoft.B Microsoft.Build.Experimental.MSBuildClientExitType.Success = 0 -> Microsoft.Build.Experimental.MSBuildClientExitType Microsoft.Build.Experimental.MSBuildClientExitType.Unexpected = 4 -> Microsoft.Build.Experimental.MSBuildClientExitType Microsoft.Build.Experimental.OutOfProcServerNode -Microsoft.Build.Experimental.OutOfProcServerNode.OutOfProcServerNode(System.Func buildFunction) -> void +Microsoft.Build.Experimental.OutOfProcServerNode.BuildCallback +Microsoft.Build.Experimental.OutOfProcServerNode.OutOfProcServerNode(Microsoft.Build.Experimental.OutOfProcServerNode.BuildCallback buildFunction) -> void Microsoft.Build.Experimental.OutOfProcServerNode.Run(out System.Exception shutdownException) -> Microsoft.Build.Execution.NodeEngineShutdownReason diff --git a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt index da542899bb9..20875ae93b2 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.Experimental.MSBuildClient -Microsoft.Build.Experimental.MSBuildClient.Execute(string commandLine, System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Experimental.MSBuildClientExitResult -Microsoft.Build.Experimental.MSBuildClient.MSBuildClient(string msbuildLocation) -> void +Microsoft.Build.Experimental.MSBuildClient.Execute(System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Experimental.MSBuildClientExitResult +Microsoft.Build.Experimental.MSBuildClient.MSBuildClient(string[] commandLine, string msbuildLocation) -> void Microsoft.Build.Experimental.MSBuildClientExitResult Microsoft.Build.Experimental.MSBuildClientExitResult.MSBuildAppExitTypeString.get -> string Microsoft.Build.Experimental.MSBuildClientExitResult.MSBuildAppExitTypeString.set -> void @@ -15,5 +15,6 @@ Microsoft.Build.Experimental.MSBuildClientExitType.ServerBusy = 1 -> Microsoft.B Microsoft.Build.Experimental.MSBuildClientExitType.Success = 0 -> Microsoft.Build.Experimental.MSBuildClientExitType Microsoft.Build.Experimental.MSBuildClientExitType.Unexpected = 4 -> Microsoft.Build.Experimental.MSBuildClientExitType Microsoft.Build.Experimental.OutOfProcServerNode -Microsoft.Build.Experimental.OutOfProcServerNode.OutOfProcServerNode(System.Func buildFunction) -> void +Microsoft.Build.Experimental.OutOfProcServerNode.BuildCallback +Microsoft.Build.Experimental.OutOfProcServerNode.OutOfProcServerNode(Microsoft.Build.Experimental.OutOfProcServerNode.BuildCallback buildFunction) -> void Microsoft.Build.Experimental.OutOfProcServerNode.Run(out System.Exception shutdownException) -> Microsoft.Build.Execution.NodeEngineShutdownReason diff --git a/src/MSBuild/MSBuildClientApp.cs b/src/MSBuild/MSBuildClientApp.cs index fb6a1fa4f02..b5187d198a1 100644 --- a/src/MSBuild/MSBuildClientApp.cs +++ b/src/MSBuild/MSBuildClientApp.cs @@ -46,9 +46,8 @@ CancellationToken cancellationToken return Execute( commandLine, - cancellationToken, - msbuildLocation - ); + msbuildLocation, + cancellationToken); } /// @@ -57,9 +56,9 @@ CancellationToken cancellationToken /// The command line to process. The first argument /// on the command line is assumed to be the name/path of the executable, and /// is ignored. - /// Cancellation token. /// 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. + /// Cancellation token. /// A value of type that indicates whether the build succeeded, /// or the manner in which it failed. public static MSBuildApp.ExitType Execute( @@ -68,18 +67,11 @@ public static MSBuildApp.ExitType Execute( #else string[] commandLine, #endif - CancellationToken cancellationToken, - string msbuildLocation - ) + string msbuildLocation, + CancellationToken cancellationToken) { - // MSBuild client orchestration. -#if !FEATURE_GET_COMMANDLINE - string commandLineString = string.Join(" ", commandLine); -#else - string commandLineString = commandLine; -#endif - MSBuildClient msbuildClient = new MSBuildClient(msbuildLocation); - MSBuildClientExitResult exitResult = msbuildClient.Execute(commandLineString, cancellationToken); + MSBuildClient msbuildClient = new MSBuildClient(commandLine, msbuildLocation); + MSBuildClientExitResult exitResult = msbuildClient.Execute(cancellationToken); if (exitResult.MSBuildClientExitType == MSBuildClientExitType.ServerBusy || exitResult.MSBuildClientExitType == MSBuildClientExitType.ConnectionError) diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index ebe6acb78af..33b9956cff1 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -2679,7 +2679,7 @@ private static void StartLocalNode(CommandLineSwitches commandLineSwitches, bool { // Since build function has to reuse code from *this* class and OutOfProcServerNode is in different assembly // we have to pass down xmake build invocation to avoid circular dependency - Func buildFunction = (commandLine) => + OutOfProcServerNode.BuildCallback buildFunction = (commandLine) => { int exitCode; ExitType exitType; @@ -2690,13 +2690,7 @@ private static void StartLocalNode(CommandLineSwitches commandLineSwitches, bool } else { - exitType = Execute( -#if FEATURE_GET_COMMANDLINE - commandLine -#else - QuotingUtilities.SplitUnquoted(commandLine).ToArray() -#endif - ); + exitType = Execute(commandLine); exitCode = exitType == ExitType.Success ? 0 : 1; } exitCode = exitType == ExitType.Success ? 0 : 1;