/// <summary> /// Factory for deserialization. /// </summary> internal static INodePacket FactoryForDeserialization(INodePacketTranslator translator) { TaskHostTaskComplete taskComplete = new TaskHostTaskComplete(); taskComplete.Translate(translator); return(taskComplete); }
/// <summary> /// Task completed executing in the task host /// </summary> private void HandleTaskHostTaskComplete(TaskHostTaskComplete taskHostTaskComplete) { // If it crashed, or if it failed, it didn't succeed. _taskExecutionSucceeded = taskHostTaskComplete.TaskResult == TaskCompleteType.Success ? true : false; // reset the environment, as though the task were executed in this process all along. CommunicationsUtilities.SetEnvironment(taskHostTaskComplete.BuildProcessEnvironment); // If it crashed during the execution phase, then we can effectively replicate the inproc task execution // behaviour by just throwing here and letting the taskbuilder code take care of it the way it would // have normally. // We will also replicate the same behaviour if the TaskHost caught some exceptions after execution of the task. if ((taskHostTaskComplete.TaskResult == TaskCompleteType.CrashedDuringExecution) || (taskHostTaskComplete.TaskResult == TaskCompleteType.CrashedAfterExecution)) { throw new TargetInvocationException(taskHostTaskComplete.TaskException); } // On the other hand, if it crashed during initialization, there's not really a way to effectively replicate // the inproc behavior -- in the inproc case, the task would have failed to load and crashed long before now. // Furthermore, if we were just to throw here like in the execution case, we'd lose the ability to log // different messages based on the circumstances of the initialization failure -- whether it was a setter failure, // the task just could not be loaded, etc. // So instead, when we catch the exception in the task host, we'll also record what message we want it to use // when the error is logged; and given that information, log that error here. This has the effect of differing // from the inproc case insofar as ContinueOnError is now respected, instead of forcing a stop here. if (taskHostTaskComplete.TaskResult == TaskCompleteType.CrashedDuringInitialization) { string exceptionMessage; string[] exceptionMessageArgs; if (taskHostTaskComplete.TaskExceptionMessage != null) { exceptionMessage = taskHostTaskComplete.TaskExceptionMessage; exceptionMessageArgs = taskHostTaskComplete.TaskExceptionMessageArgs; } else { exceptionMessage = "TaskInstantiationFailureError"; exceptionMessageArgs = new string[] { _taskType.Type.Name, AssemblyUtilities.GetAssemblyLocation(_taskType.Type.GetTypeInfo().Assembly), string.Empty }; } _taskLoggingContext.LogFatalError(taskHostTaskComplete.TaskException, new BuildEventFileInfo(_taskLocation), taskHostTaskComplete.TaskExceptionMessage, taskHostTaskComplete.TaskExceptionMessageArgs); } // Set the output parameters for later foreach (KeyValuePair <string, TaskParameter> outputParam in taskHostTaskComplete.TaskOutputParameters) { _setParameters[outputParam.Key] = outputParam.Value?.WrappedParameter; } }
public void TestTranslationWithNullDictionary() { TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success), null); ((INodePacketTranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator()); INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); TaskHostTaskComplete deserializedComplete = packet as TaskHostTaskComplete; Assert.AreEqual(complete.TaskResult, deserializedComplete.TaskResult); Assert.IsNotNull(deserializedComplete.TaskOutputParameters); Assert.AreEqual(0, deserializedComplete.TaskOutputParameters.Count); }
public void TestConstructors() { TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success), null); TaskHostTaskComplete complete2 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure), null); TaskHostTaskComplete complete3 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringInitialization, new ArgumentOutOfRangeException()), null); TaskHostTaskComplete complete4 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringExecution, new ArgumentNullException()), null); IDictionary<string, object> parameters = new Dictionary<string, object>(); TaskHostTaskComplete complete5 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters), null); IDictionary<string, object> parameters2 = new Dictionary<string, object>(); parameters2.Add("Text", "Hello!"); parameters2.Add("MyBoolValue", true); parameters2.Add("MyITaskItem", new TaskItem("ABC")); parameters2.Add("ItemArray", new ITaskItem[] { new TaskItem("DEF"), new TaskItem("GHI"), new TaskItem("JKL") }); TaskHostTaskComplete complete6 = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters2), null); }
/// <summary> /// Task runner method /// </summary> private void RunTask(object state) { _isTaskExecuting = true; OutOfProcTaskHostTaskResult taskResult = null; TaskHostConfiguration taskConfiguration = state as TaskHostConfiguration; IDictionary<string, TaskParameter> taskParams = taskConfiguration.TaskParameters; // We only really know the values of these variables for sure once we see what we received from our parent // environment -- otherwise if this was a completely new build, we could lose out on expected environment // variables. _debugCommunications = taskConfiguration.BuildProcessEnvironment.ContainsValueAndIsEqual("MSBUILDDEBUGCOMM", "1", StringComparison.OrdinalIgnoreCase); _updateEnvironment = !taskConfiguration.BuildProcessEnvironment.ContainsValueAndIsEqual("MSBuildTaskHostDoNotUpdateEnvironment", "1", StringComparison.OrdinalIgnoreCase); _updateEnvironmentAndLog = taskConfiguration.BuildProcessEnvironment.ContainsValueAndIsEqual("MSBuildTaskHostUpdateEnvironmentAndLog", "1", StringComparison.OrdinalIgnoreCase); try { // Change to the startup directory NativeMethodsShared.SetCurrentDirectory(taskConfiguration.StartupDirectory); if (_updateEnvironment) { InitializeMismatchedEnvironmentTable(taskConfiguration.BuildProcessEnvironment); } // Now set the new environment SetTaskHostEnvironment(taskConfiguration.BuildProcessEnvironment); // Set culture Thread.CurrentThread.CurrentCulture = taskConfiguration.Culture; Thread.CurrentThread.CurrentUICulture = taskConfiguration.UICulture; string taskName = taskConfiguration.TaskName; string taskLocation = taskConfiguration.TaskLocation; // We will not create an appdomain now because of a bug // As a fix, we will create the class directly without wrapping it in a domain _taskWrapper = new OutOfProcTaskAppDomainWrapper(); taskResult = _taskWrapper.ExecuteTask ( this as IBuildEngine, taskName, taskLocation, taskConfiguration.ProjectFileOfTask, taskConfiguration.LineNumberOfTask, taskConfiguration.ColumnNumberOfTask, taskConfiguration.AppDomainSetup, taskParams ); } catch (Exception e) { if (e is ThreadAbortException) { // This thread was aborted as part of Cancellation, we will return a failure task result taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure); } else if (ExceptionHandling.IsCriticalException(e)) { throw; } else { taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringExecution, e); } } finally { try { _isTaskExecuting = false; IDictionary<string, string> currentEnvironment = CommunicationsUtilities.GetEnvironmentVariables(); currentEnvironment = UpdateEnvironmentForMainNode(currentEnvironment); if (taskResult == null) { taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure); } lock (_taskCompleteLock) { _taskCompletePacket = new TaskHostTaskComplete ( taskResult, currentEnvironment ); } foreach (TaskParameter param in taskParams.Values) { // Tell remoting to forget connections to the parameter RemotingServices.Disconnect(param); } // Restore the original clean environment CommunicationsUtilities.SetEnvironment(_savedEnvironment); } catch (Exception e) { lock (_taskCompleteLock) { // Create a minimal taskCompletePacket to carry the exception so that the TaskHostTask does not hang while waiting _taskCompletePacket = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedAfterExecution, e), null); } } finally { // Call CleanupTask to unload any domains and other necessary cleanup in the taskWrapper _taskWrapper.CleanupTask(); // The task has now fully completed executing _taskCompleteEvent.Set(); } } }
/// <summary> /// The task has been completed /// </summary> private void CompleteTask() { ErrorUtilities.VerifyThrow(_isTaskExecuting == false, "The task should be done executing before CompleteTask."); if (_nodeEndpoint.LinkStatus == LinkStatus.Active) { TaskHostTaskComplete taskCompletePacketToSend; lock (_taskCompleteLock) { ErrorUtilities.VerifyThrowInternalNull(_taskCompletePacket, "taskCompletePacket"); taskCompletePacketToSend = _taskCompletePacket; _taskCompletePacket = null; } _nodeEndpoint.SendData(taskCompletePacketToSend); } _currentConfiguration = null; // If the task has been canceled, the event will still be set. // If so, now that we've completed the task, we want to shut down // this node -- with no reuse, since we don't know whether the // task we canceled left the node in a good state or not. if (_taskCancelledEvent.WaitOne(0, false)) { _shutdownReason = NodeEngineShutdownReason.BuildComplete; _shutdownEvent.Set(); } }
/// <summary> /// Helper method for testing invalid constructors /// </summary> private void AssertInvalidConstructorThrows(Type expectedExceptionType, TaskCompleteType taskResult, Exception taskException, string taskExceptionMessage, string[] taskExceptionMessageArgs, IDictionary<string, object> taskOutputParameters, IDictionary<string, string> buildProcessEnvironment) { bool exceptionCaught = false; try { TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(taskResult, taskOutputParameters, taskException, taskExceptionMessage, taskExceptionMessageArgs), buildProcessEnvironment); } catch (Exception e) { exceptionCaught = true; Assert.IsInstanceOfType(e, expectedExceptionType, "Wrong exception was thrown!"); } Assert.IsTrue(exceptionCaught, "No exception was caught when one was expected!"); }
public void TestTranslationWithITaskItemArrayInDictionary() { IDictionary<string, object> parameters = new Dictionary<string, object>(); parameters.Add("TaskItemArrayValue", new ITaskItem[] { new TaskItem("Foo"), new TaskItem("Baz") }); TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters), null); ((INodePacketTranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator()); INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); TaskHostTaskComplete deserializedComplete = packet as TaskHostTaskComplete; Assert.AreEqual(complete.TaskResult, deserializedComplete.TaskResult); Assert.IsNotNull(deserializedComplete.TaskOutputParameters); Assert.AreEqual(complete.TaskOutputParameters.Count, deserializedComplete.TaskOutputParameters.Count); ITaskItem[] itemArray = (ITaskItem[])complete.TaskOutputParameters["TaskItemArrayValue"].WrappedParameter; ITaskItem[] deserializedItemArray = (ITaskItem[])deserializedComplete.TaskOutputParameters["TaskItemArrayValue"].WrappedParameter; TaskHostPacketHelpers.AreEqual(itemArray, deserializedItemArray); }
public void TestTranslationWithValueTypesInDictionary() { IDictionary<string, object> parameters = new Dictionary<string, object>(); parameters.Add("Text", "Foo"); parameters.Add("BoolValue", false); TaskHostTaskComplete complete = new TaskHostTaskComplete(new OutOfProcTaskHostTaskResult(TaskCompleteType.Success, parameters), null); ((INodePacketTranslatable)complete).Translate(TranslationHelpers.GetWriteTranslator()); INodePacket packet = TaskHostTaskComplete.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); TaskHostTaskComplete deserializedComplete = packet as TaskHostTaskComplete; Assert.AreEqual(complete.TaskResult, deserializedComplete.TaskResult); Assert.IsNotNull(deserializedComplete.TaskOutputParameters); Assert.AreEqual(complete.TaskOutputParameters.Count, deserializedComplete.TaskOutputParameters.Count); Assert.AreEqual(complete.TaskOutputParameters["Text"].WrappedParameter, deserializedComplete.TaskOutputParameters["Text"].WrappedParameter); Assert.AreEqual(complete.TaskOutputParameters["BoolValue"].WrappedParameter, deserializedComplete.TaskOutputParameters["BoolValue"].WrappedParameter); }
/// <summary> /// Task completed executing in the task host /// </summary> private void HandleTaskHostTaskComplete(TaskHostTaskComplete taskHostTaskComplete) { // If it crashed, or if it failed, it didn't succeed. _taskExecutionSucceeded = taskHostTaskComplete.TaskResult == TaskCompleteType.Success ? true : false; // reset the environment, as though the task were executed in this process all along. CommunicationsUtilities.SetEnvironment(taskHostTaskComplete.BuildProcessEnvironment); // If it crashed during the execution phase, then we can effectively replicate the inproc task execution // behaviour by just throwing here and letting the taskbuilder code take care of it the way it would // have normally. // We will also replicate the same behaviour if the TaskHost caught some exceptions after execution of the task. if ((taskHostTaskComplete.TaskResult == TaskCompleteType.CrashedDuringExecution) || (taskHostTaskComplete.TaskResult == TaskCompleteType.CrashedAfterExecution)) { throw new TargetInvocationException(taskHostTaskComplete.TaskException); } // On the other hand, if it crashed during initialization, there's not really a way to effectively replicate // the inproc behavior -- in the inproc case, the task would have failed to load and crashed long before now. // Furthermore, if we were just to throw here like in the execution case, we'd lose the ability to log // different messages based on the circumstances of the initialization failure -- whether it was a setter failure, // the task just could not be loaded, etc. // So instead, when we catch the exception in the task host, we'll also record what message we want it to use // when the error is logged; and given that information, log that error here. This has the effect of differing // from the inproc case insofar as ContinueOnError is now respected, instead of forcing a stop here. if (taskHostTaskComplete.TaskResult == TaskCompleteType.CrashedDuringInitialization) { string exceptionMessage; string[] exceptionMessageArgs; if (taskHostTaskComplete.TaskExceptionMessage != null) { exceptionMessage = taskHostTaskComplete.TaskExceptionMessage; exceptionMessageArgs = taskHostTaskComplete.TaskExceptionMessageArgs; } else { exceptionMessage = "TaskInstantiationFailureError"; exceptionMessageArgs = new string[] { _taskType.Type.Name, _taskType.Type.Assembly.Location, String.Empty }; } _taskLoggingContext.LogFatalError(new BuildEventFileInfo(_taskLocation), taskHostTaskComplete.TaskException, taskHostTaskComplete.TaskExceptionMessage, taskHostTaskComplete.TaskExceptionMessageArgs); } // Set the output parameters for later foreach (KeyValuePair<string, TaskParameter> outputParam in taskHostTaskComplete.TaskOutputParameters) { _setParameters[outputParam.Key] = (outputParam.Value == null) ? null : outputParam.Value.WrappedParameter; } }
/// <summary> /// Factory for deserialization. /// </summary> internal static INodePacket FactoryForDeserialization(INodePacketTranslator translator) { TaskHostTaskComplete taskComplete = new TaskHostTaskComplete(); taskComplete.Translate(translator); return taskComplete; }