From 8080b4c167af3a46db4092eac4a71e1b3545a969 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 27 May 2022 10:44:14 +0200 Subject: [PATCH 1/4] Solving memory leak by reusing BuildManager and ProjectRoolElementCache --- src/Build/Definition/ProjectCollection.cs | 43 ++++++++++++++++++- .../PublicAPI/net/PublicAPI.Unshipped.txt | 1 + .../netstandard/PublicAPI.Unshipped.txt | 1 + src/MSBuild/XMake.cs | 12 +++++- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/Build/Definition/ProjectCollection.cs b/src/Build/Definition/ProjectCollection.cs index 485b905abe0..096fcc073a4 100644 --- a/src/Build/Definition/ProjectCollection.cs +++ b/src/Build/Definition/ProjectCollection.cs @@ -145,6 +145,8 @@ public void Dispose() /// private static string s_assemblyDisplayVersion; + private static ProjectRootElementCacheBase s_projectRootElementCache = null; + /// /// The projects loaded into this collection. /// @@ -302,6 +304,26 @@ public ProjectCollection(IDictionary globalProperties, IEnumerab /// If set to true, only critical events will be logged. /// If set to true, load all projects as read-only. public ProjectCollection(IDictionary globalProperties, IEnumerable loggers, IEnumerable remoteLoggers, ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly) + : this(globalProperties, loggers, remoteLoggers, toolsetDefinitionLocations, maxNodeCount, onlyLogCriticalEvents, loadProjectsReadOnly, reuseProjectRootElementCache: false) + { + } + + /// + /// Instantiates a project collection with specified global properties and loggers and using the + /// specified toolset locations, node count, and setting of onlyLogCriticalEvents. + /// Global properties and loggers may be null. + /// Throws InvalidProjectFileException if any of the global properties are reserved. + /// May throw InvalidToolsetDefinitionException. + /// + /// The default global properties to use. May be null. + /// The loggers to register. May be null and specified to any build instead. + /// Any remote loggers to register. May be null and specified to any build instead. + /// The locations from which to load toolsets. + /// The maximum number of nodes to use for building. + /// If set to true, only critical events will be logged. + /// If set to true, load all projects as read-only. + /// If set to true, it will try to reuse singleton. + public ProjectCollection(IDictionary globalProperties, IEnumerable loggers, IEnumerable remoteLoggers, ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly, bool reuseProjectRootElementCache) { _loadedProjects = new LoadedProjectCollection(); ToolsetLocations = toolsetDefinitionLocations; @@ -311,10 +333,23 @@ public ProjectCollection(IDictionary globalProperties, IEnumerab { ProjectRootElementCache = new SimpleProjectRootElementCache(); } + else if (reuseProjectRootElementCache && s_projectRootElementCache != null) + { + ProjectRootElementCache = s_projectRootElementCache; + } else { - ProjectRootElementCache = new ProjectRootElementCache(autoReloadFromDisk: false, loadProjectsReadOnly); + // When we are reusing ProjectRootElementCache we need to reload XMLs if it has changed between MSBuild Server sessions/builds. + // If we are not reusing, cache will be released at end of build and as we do not support project files will changes during build + // we do not need to auto reload. + bool autoReloadFromDisk = reuseProjectRootElementCache; + ProjectRootElementCache = new ProjectRootElementCache(autoReloadFromDisk, loadProjectsReadOnly); + if (reuseProjectRootElementCache && s_projectRootElementCache == null) + { + s_projectRootElementCache = ProjectRootElementCache; + } } + OnlyLogCriticalEvents = onlyLogCriticalEvents; try @@ -1603,6 +1638,12 @@ protected virtual void Dispose(bool disposing) if (disposing) { ShutDownLoggingService(); + if (ProjectRootElementCache != null) + { + ProjectRootElementCache.ProjectRootElementAddedHandler -= ProjectRootElementCache_ProjectRootElementAddedHandler; + ProjectRootElementCache.ProjectRootElementDirtied -= ProjectRootElementCache_ProjectRootElementDirtiedHandler; + ProjectRootElementCache.ProjectDirtied -= ProjectRootElementCache_ProjectDirtiedHandler; + } Tracing.Dump(); } } diff --git a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt index 349a8e57aac..ee20877adfb 100644 --- a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +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 diff --git a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt index 39c901f1b5c..44179d2f0e1 100644 --- a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +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 diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index b7d98c179d5..37434c6fc65 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -1082,7 +1082,8 @@ string[] commandLine toolsetDefinitionLocations, cpuCount, onlyLogCriticalEvents, - loadProjectsReadOnly: !preprocessOnly + loadProjectsReadOnly: !preprocessOnly, + reuseProjectRootElementCache: s_isServerNode ); if (toolsVersion != null && !projectCollection.ContainsToolset(toolsVersion)) @@ -1315,7 +1316,14 @@ string[] commandLine FileUtilities.ClearCacheDirectory(); projectCollection?.Dispose(); - BuildManager.DefaultBuildManager.Dispose(); + // Build manager shall be reused for all build sessions. + // If, for one reason or another, this behavior needs to change in future + // please be aware that current code creates and keep running InProcNode even + // when its owning default build manager is disposed resulting in leek of memory and threads. + if (!s_isServerNode) + { + BuildManager.DefaultBuildManager.Dispose(); + } } return success; From e41cf8a6ff737fe197b89d7c856444a9f39b89b6 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 27 May 2022 12:13:16 +0200 Subject: [PATCH 2/4] Do not clear project root element cache if in auto reload. --- src/Build/Evaluation/ProjectRootElementCache.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Build/Evaluation/ProjectRootElementCache.cs b/src/Build/Evaluation/ProjectRootElementCache.cs index 17ecae43227..208a43ed668 100644 --- a/src/Build/Evaluation/ProjectRootElementCache.cs +++ b/src/Build/Evaluation/ProjectRootElementCache.cs @@ -415,6 +415,12 @@ internal override void Clear() /// internal override void DiscardImplicitReferences() { + if (_autoReloadFromDisk) + { + // no need to clear it, as auto reload properly invalidates caches if changed. + return; + } + lock (_locker) { // Make a new Weak cache only with items that have been explicitly loaded, this will be a small number, there will most likely From 447225c121b96cdadf7bec6ca0e8d2ffb15900e2 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 27 May 2022 22:02:32 +0200 Subject: [PATCH 3/4] Reduce if Co-authored-by: Forgind --- src/Build/Definition/ProjectCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/Definition/ProjectCollection.cs b/src/Build/Definition/ProjectCollection.cs index 096fcc073a4..f5d45290cd2 100644 --- a/src/Build/Definition/ProjectCollection.cs +++ b/src/Build/Definition/ProjectCollection.cs @@ -344,7 +344,7 @@ public ProjectCollection(IDictionary globalProperties, IEnumerab // we do not need to auto reload. bool autoReloadFromDisk = reuseProjectRootElementCache; ProjectRootElementCache = new ProjectRootElementCache(autoReloadFromDisk, loadProjectsReadOnly); - if (reuseProjectRootElementCache && s_projectRootElementCache == null) + if (reuseProjectRootElementCache) { s_projectRootElementCache = ProjectRootElementCache; } From 5670086a7a8b948c7bb48f23f5d0db91b0d14ab0 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Tue, 21 Jun 2022 15:59:36 +0200 Subject: [PATCH 4/4] Send command line as string[] in dotnet builds. --- src/Build/BackEnd/Client/MSBuildClient.cs | 48 ++++++++++++++----- src/Build/BackEnd/Node/OutOfProcServerNode.cs | 14 +++++- .../BackEnd/Node/ServerNodeBuildCommand.cs | 20 +++++++- .../PublicAPI/net/PublicAPI.Unshipped.txt | 7 +-- .../netstandard/PublicAPI.Unshipped.txt | 7 +-- src/MSBuild/MSBuildClientApp.cs | 22 +++------ src/MSBuild/XMake.cs | 10 +--- 7 files changed, 83 insertions(+), 45 deletions(-) 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;