/// <summary> /// Expected to be called when TaskHostTask is done with host of the given context. /// </summary> internal void DisconnectFromHost(TaskHostContext hostContext) { ErrorUtilities.VerifyThrow(_nodeIdToPacketFactory.ContainsKey((int)hostContext) && _nodeIdToPacketHandler.ContainsKey((int)hostContext), "Why are we trying to disconnect from a context that we already disconnected from? Did we call DisconnectFromHost twice?"); _nodeIdToPacketFactory.Remove((int)hostContext); _nodeIdToPacketHandler.Remove((int)hostContext); }
/// <summary> /// Instantiates a new MSBuild or MSBuildTaskHost process acting as a child node. /// </summary> internal bool CreateNode(TaskHostContext hostContext, INodePacketFactory factory, INodePacketHandler handler, TaskHostConfiguration configuration) { ErrorUtilities.VerifyThrowArgumentNull(factory, "factory"); ErrorUtilities.VerifyThrow(!_nodeIdToPacketFactory.ContainsKey((int)hostContext), "We should not already have a factory for this context! Did we forget to call DisconnectFromHost somewhere?"); if (AvailableNodes == 0) { ErrorUtilities.ThrowInternalError("All allowable nodes already created ({0}).", _nodeContexts.Count); return false; } // Start the new process. We pass in a node mode with a node number of 2, to indicate that we // want to start up an MSBuild task host node. string commandLineArgs = " /nologo /nodemode:2 "; string msbuildLocation = GetMSBuildLocationFromHostContext(hostContext); // we couldn't even figure out the location we're trying to launch ... just go ahead and fail. if (msbuildLocation == null) { return false; } CommunicationsUtilities.Trace("For a host context of {0}, spawning executable from {1}.", hostContext.ToString(), msbuildLocation ?? "MSBuild.exe"); // Make it here. NodeContext context = GetNode ( msbuildLocation, commandLineArgs, (int)hostContext, this, CommunicationsUtilities.GetTaskHostHostHandshake(hostContext), CommunicationsUtilities.GetTaskHostClientHandshake(hostContext), NodeContextTerminated ); if (null != context) { _nodeContexts[hostContext] = context; // Start the asynchronous read. context.BeginAsyncPacketRead(); _activeNodes.Add((int)hostContext); _noNodesActiveEvent.Reset(); return true; } return false; }
/// <summary> /// Given a TaskHostContext, return the appropriate location of the /// executable (MSBuild or MSBuildTaskHost) that we wish to use, or null /// if that location cannot be resolved. /// </summary> internal static string GetMSBuildLocationFromHostContext(TaskHostContext hostContext) { string toolName = GetTaskHostNameFromHostContext(hostContext); string toolPath = null; if (s_baseTaskHostPath == null) { Toolset currentToolset = ProjectCollection.GlobalProjectCollection.GetToolset(MSBuildConstants.CurrentToolsVersion); if (currentToolset != null) { ProjectPropertyInstance toolsPath32 = null; if (!currentToolset.Properties.TryGetValue("MSBuildToolsPath32", out toolsPath32)) { // ... This is a very weird toolset. But try falling back to the base ToolsPath since we // know for a fact that that will always exist. s_baseTaskHostPath = currentToolset.ToolsPath; } else { s_baseTaskHostPath = toolsPath32.EvaluatedValue; } } } if (s_baseTaskHostPath64 == null && s_baseTaskHostPath != null) { s_baseTaskHostPath64 = Path.Combine(s_baseTaskHostPath, "amd64"); if (!Directory.Exists(s_baseTaskHostPath64)) { // time to give up s_baseTaskHostPath64 = null; } } switch (hostContext) { case TaskHostContext.X32CLR2: if (s_pathToX32Clr2 == null) { s_pathToX32Clr2 = Environment.GetEnvironmentVariable("MSBUILDTASKHOSTLOCATION"); if (s_pathToX32Clr2 == null || !FileUtilities.FileExistsNoThrow(Path.Combine(s_pathToX32Clr2, toolName))) { s_pathToX32Clr2 = s_baseTaskHostPath; } } toolPath = s_pathToX32Clr2; break; case TaskHostContext.X64CLR2: if (s_pathToX64Clr2 == null) { s_pathToX64Clr2 = Environment.GetEnvironmentVariable("MSBUILDTASKHOSTLOCATION64"); if (s_pathToX64Clr2 == null || !FileUtilities.FileExistsNoThrow(Path.Combine(s_pathToX64Clr2, toolName))) { s_pathToX64Clr2 = s_baseTaskHostPath64; } } toolPath = s_pathToX64Clr2; break; case TaskHostContext.X32CLR4: if (s_pathToX32Clr4 == null) { s_pathToX32Clr4 = s_baseTaskHostPath; } toolPath = s_pathToX32Clr4; break; case TaskHostContext.X64CLR4: if (s_pathToX64Clr4 == null) { s_pathToX64Clr4 = s_baseTaskHostPath64; } toolPath = s_pathToX64Clr4; break; default: ErrorUtilities.ThrowInternalErrorUnreachable(); break; } if (toolName != null && toolPath != null) { return Path.Combine(toolPath, toolName); } else { return null; } }
/// <summary> /// Make sure a node in the requested context exists. /// </summary> internal bool AcquireAndSetUpHost(TaskHostContext hostContext, INodePacketFactory factory, INodePacketHandler handler, TaskHostConfiguration configuration) { NodeContext context = null; bool nodeCreationSucceeded = false; if (!(_nodeContexts.TryGetValue(hostContext, out context))) { nodeCreationSucceeded = CreateNode(hostContext, factory, handler, configuration); } else { // node already exists, so "creation" automatically succeeded nodeCreationSucceeded = true; } if (nodeCreationSucceeded) { context = _nodeContexts[hostContext]; _nodeIdToPacketFactory[(int)hostContext] = factory; _nodeIdToPacketHandler[(int)hostContext] = handler; // Configure the node. context.SendData(configuration); return true; } return false; }
/// <summary> /// Sends data to the specified node. /// </summary> /// <param name="hostContext">The node to which data shall be sent.</param> /// <param name="packet">The packet to send.</param> public void SendData(TaskHostContext hostContext, INodePacket packet) { ErrorUtilities.VerifyThrow(_nodeContexts.ContainsKey(hostContext), "Invalid host context specified: {0}.", hostContext.ToString()); SendData(_nodeContexts[hostContext], packet); }
/// <summary> /// Given a TaskHostContext, returns the name of the executable we should be searching for. /// </summary> internal static string GetTaskHostNameFromHostContext(TaskHostContext hostContext) { if (hostContext == TaskHostContext.X64CLR4 || hostContext == TaskHostContext.X32CLR4) { if (s_msbuildName == null) { s_msbuildName = Environment.GetEnvironmentVariable("MSBUILD_EXE_NAME"); if (s_msbuildName == null) { s_msbuildName = "MSBuild.exe"; } } return s_msbuildName; } else if (hostContext == TaskHostContext.X32CLR2 || hostContext == TaskHostContext.X64CLR2) { return TaskHostNameForClr2TaskHost; } else { ErrorUtilities.ThrowInternalErrorUnreachable(); return null; } }
/// <summary> /// Add the task host context to this handshake, to make sure that task hosts with different contexts /// will have different handshakes. Shift it into the upper 32-bits to avoid running into the /// session ID. /// </summary> /// <param name="hostContext">TaskHostContext</param> /// <returns>Base Handshake</returns> private static long GetBaseHandshakeForContext(TaskHostContext hostContext) { long baseHandshake = ((long)hostContext << 40) | ((long)FileVersionHash << 8); return baseHandshake; }
/// <summary> /// Given a TaskHostContext, return the appropriate location of the /// executable (MSBuild or MSBuildTaskHost) that we wish to use, or null /// if that location cannot be resolved. /// </summary> internal static string GetMSBuildLocationFromHostContext(TaskHostContext hostContext) { string toolName = GetTaskHostNameFromHostContext(hostContext); string toolPath = null; s_baseTaskHostPath = BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32; s_baseTaskHostPath64 = BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64; switch (hostContext) { case TaskHostContext.X32CLR2: if (s_pathToX32Clr2 == null) { s_pathToX32Clr2 = Environment.GetEnvironmentVariable("MSBUILDTASKHOSTLOCATION"); if (s_pathToX32Clr2 == null || !FileUtilities.FileExistsNoThrow(Path.Combine(s_pathToX32Clr2, toolName))) { s_pathToX32Clr2 = s_baseTaskHostPath; } } toolPath = s_pathToX32Clr2; break; case TaskHostContext.X64CLR2: if (s_pathToX64Clr2 == null) { s_pathToX64Clr2 = Environment.GetEnvironmentVariable("MSBUILDTASKHOSTLOCATION64"); if (s_pathToX64Clr2 == null || !FileUtilities.FileExistsNoThrow(Path.Combine(s_pathToX64Clr2, toolName))) { s_pathToX64Clr2 = s_baseTaskHostPath64; } } toolPath = s_pathToX64Clr2; break; case TaskHostContext.X32CLR4: if (s_pathToX32Clr4 == null) { s_pathToX32Clr4 = s_baseTaskHostPath; } toolPath = s_pathToX32Clr4; break; case TaskHostContext.X64CLR4: if (s_pathToX64Clr4 == null) { s_pathToX64Clr4 = s_baseTaskHostPath64; } toolPath = s_pathToX64Clr4; break; default: ErrorUtilities.ThrowInternalErrorUnreachable(); break; } if (toolName != null && toolPath != null) { return Path.Combine(toolPath, toolName); } return null; }
/// <summary> /// Magic number sent by the host to the client during the handshake. /// Derived from the binary timestamp to avoid mixing binary versions. /// </summary> internal static long GetTaskHostHostHandshake(TaskHostContext hostContext) { long baseHandshake = GenerateHostHandshakeFromBase(GetBaseHandshakeForContext(hostContext), GetTaskHostClientHandshake(hostContext)); return baseHandshake; }
/// <summary> /// Magic number sent by the client to the host during the handshake. /// Munged version of the host handshake. /// </summary> internal static long GetTaskHostClientHandshake(TaskHostContext hostContext) { // Mask out the first byte. That's because old // builds used a single, non zero initial byte, // and we don't want to risk communicating with them long clientHandshake = ((GetBaseHandshakeForContext(hostContext) ^ Int64.MaxValue) & 0x00FFFFFFFFFFFFFF); return clientHandshake; }
/// <summary> /// Since we log that we weren't able to connect to the task host in a couple of different places, /// extract it out into a separate method. /// </summary> private void LogErrorUnableToCreateTaskHost(TaskHostContext requiredContext, string runtime, string architecture, NodeFailedToLaunchException e) { string msbuildLocation = NodeProviderOutOfProcTaskHost.GetMSBuildLocationFromHostContext(requiredContext); if (msbuildLocation == null) { // We don't know the path -- probably we're trying to get a 64-bit assembly on a // 32-bit machine. At least give them the exe name to look for, though ... switch (requiredContext) { case TaskHostContext.X32CLR2: case TaskHostContext.X64CLR2: msbuildLocation = "MSBuildTaskHost.exe"; break; case TaskHostContext.X32CLR4: case TaskHostContext.X64CLR4: msbuildLocation = "MSBuild.exe"; break; case TaskHostContext.Invalid: default: ErrorUtilities.ThrowInternalErrorUnreachable(); break; } } if (e == null) { _taskLoggingContext.LogError(new BuildEventFileInfo(_taskLocation), "TaskHostAcquireFailed", _taskType.Type.Name, runtime, architecture, msbuildLocation); } else { _taskLoggingContext.LogError(new BuildEventFileInfo(_taskLocation), "TaskHostNodeFailedToLaunch", _taskType.Type.Name, runtime, architecture, msbuildLocation, e.ErrorCode, e.Message); } }
/// <summary> /// Executes the task. /// </summary> public bool Execute() { // log that we are about to spawn the task host string runtime = _taskHostParameters[XMakeAttributes.runtime]; string architecture = _taskHostParameters[XMakeAttributes.architecture]; _taskLoggingContext.LogComment(MessageImportance.Low, "ExecutingTaskInTaskHost", _taskType.Type.Name, _taskType.Assembly.AssemblyLocation, runtime, architecture); // set up the node lock (_taskHostLock) { _taskHostProvider = (NodeProviderOutOfProcTaskHost)_buildComponentHost.GetComponent(BuildComponentType.OutOfProcTaskHostNodeProvider); ErrorUtilities.VerifyThrowInternalNull(_taskHostProvider, "taskHostProvider"); } TaskHostConfiguration hostConfiguration = new TaskHostConfiguration ( _buildComponentHost.BuildParameters.NodeId, NativeMethodsShared.GetCurrentDirectory(), CommunicationsUtilities.GetEnvironmentVariables(), _buildComponentHost.BuildParameters.Culture, _buildComponentHost.BuildParameters.UICulture, _appDomainSetup, BuildEngine.LineNumberOfTaskNode, BuildEngine.ColumnNumberOfTaskNode, BuildEngine.ProjectFileOfTaskNode, BuildEngine.ContinueOnError, _taskType.Type.FullName, _taskType.Type.Assembly.Location, _setParameters ); try { lock (_taskHostLock) { _requiredContext = CommunicationsUtilities.GetTaskHostContext(_taskHostParameters); _connectedToTaskHost = _taskHostProvider.AcquireAndSetUpHost(_requiredContext, this, this, hostConfiguration); } if (_connectedToTaskHost) { try { bool taskFinished = false; while (!taskFinished) { _packetReceivedEvent.WaitOne(); INodePacket packet = null; int packetCount = _receivedPackets.Count; // Handle the packet that's coming in while (packetCount > 0) { lock (_receivedPackets) { if (_receivedPackets.Count > 0) { packet = _receivedPackets.Dequeue(); } else { break; } } if (packet != null) { HandlePacket(packet, out taskFinished); } } } } finally { lock (_taskHostLock) { _taskHostProvider.DisconnectFromHost(_requiredContext); _connectedToTaskHost = false; } } } else { LogErrorUnableToCreateTaskHost(_requiredContext, runtime, architecture, null); } } catch (BuildAbortedException) { LogErrorUnableToCreateTaskHost(_requiredContext, runtime, architecture, null); } catch (NodeFailedToLaunchException e) { LogErrorUnableToCreateTaskHost(_requiredContext, runtime, architecture, e); } return _taskExecutionSucceeded; }
/// <summary> /// Add the task host context to this handshake, to make sure that task hosts with different contexts /// will have different handshakes. Shift it into the upper 32-bits to avoid running into the /// session ID. /// </summary> /// <param name="hostContext">TaskHostContext</param> /// <returns>Base Handshake</returns> private static long GetBaseHandshakeForContext(TaskHostContext hostContext) { long baseHandshake = ((long)hostContext << 40) | ((long)FileVersionHash << 8); return(baseHandshake); }
/// <summary> /// Magic number sent by the host to the client during the handshake. /// Derived from the binary timestamp to avoid mixing binary versions. /// </summary> internal static long GetTaskHostHostHandshake(TaskHostContext hostContext) { long baseHandshake = GenerateHostHandshakeFromBase(GetBaseHandshakeForContext(hostContext), GetTaskHostClientHandshake(hostContext)); return(baseHandshake); }
/// <summary> /// Executes the task. /// </summary> public bool Execute() { // log that we are about to spawn the task host string runtime = _taskHostParameters[XMakeAttributes.runtime]; string architecture = _taskHostParameters[XMakeAttributes.architecture]; _taskLoggingContext.LogComment(MessageImportance.Low, "ExecutingTaskInTaskHost", _taskType.Type.Name, _taskType.Assembly.AssemblyLocation, runtime, architecture); // set up the node lock (_taskHostLock) { _taskHostProvider = (NodeProviderOutOfProcTaskHost)_buildComponentHost.GetComponent(BuildComponentType.OutOfProcTaskHostNodeProvider); ErrorUtilities.VerifyThrowInternalNull(_taskHostProvider, "taskHostProvider"); } TaskHostConfiguration hostConfiguration = new TaskHostConfiguration ( _buildComponentHost.BuildParameters.NodeId, NativeMethodsShared.GetCurrentDirectory(), CommunicationsUtilities.GetEnvironmentVariables(), _buildComponentHost.BuildParameters.Culture, _buildComponentHost.BuildParameters.UICulture, #if FEATURE_APPDOMAIN _appDomainSetup, #endif BuildEngine.LineNumberOfTaskNode, BuildEngine.ColumnNumberOfTaskNode, BuildEngine.ProjectFileOfTaskNode, BuildEngine.ContinueOnError, _taskType.Type.FullName, AssemblyUtilities.GetAssemblyLocation(_taskType.Type.GetTypeInfo().Assembly), _setParameters, new Dictionary <string, string>(_buildComponentHost.BuildParameters.GlobalProperties) ); try { lock (_taskHostLock) { _requiredContext = CommunicationsUtilities.GetTaskHostContext(_taskHostParameters); _connectedToTaskHost = _taskHostProvider.AcquireAndSetUpHost(_requiredContext, this, this, hostConfiguration); } if (_connectedToTaskHost) { try { bool taskFinished = false; while (!taskFinished) { _packetReceivedEvent.WaitOne(); INodePacket packet = null; // Handle the packet that's coming in while (_receivedPackets.TryDequeue(out packet)) { if (packet != null) { HandlePacket(packet, out taskFinished); } } } } finally { lock (_taskHostLock) { _taskHostProvider.DisconnectFromHost(_requiredContext); _connectedToTaskHost = false; } } } else { LogErrorUnableToCreateTaskHost(_requiredContext, runtime, architecture, null); } } catch (BuildAbortedException) { LogErrorUnableToCreateTaskHost(_requiredContext, runtime, architecture, null); } catch (NodeFailedToLaunchException e) { LogErrorUnableToCreateTaskHost(_requiredContext, runtime, architecture, e); } return(_taskExecutionSucceeded); }