public void TearDownAttribute() { engine.Shutdown(); engineProxy = null; engineProxy2 = null; engine = null; }
public void SetUp() { // Whole bunch of setup code. XmlElement taskNode = new XmlDocument().CreateElement("MockTask"); LoadedType taskClass = new LoadedType(typeof(MockTask), new AssemblyLoadInfo(typeof(MockTask).Assembly.FullName, null)); engine = new Engine(@"c:\"); Project project = new Project(engine); EngineCallback engineCallback = new EngineCallback(engine); taskExecutionModule = new MockTaskExecutionModule(engineCallback); int handleId = engineCallback.CreateTaskContext(project, null, null, taskNode, EngineCallback.inProcNode, new BuildEventContext(BuildEventContext.InvalidNodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId)); TaskEngine taskEngine = new TaskEngine ( taskNode, null, /* host object */ "In Memory", project.FullFileName, engine.LoggingServices, handleId, taskExecutionModule, null ); taskEngine.TaskClass = taskClass; engineProxy = new EngineProxy(taskExecutionModule, handleId, project.FullFileName, project.FullFileName, engine.LoggingServices, null); taskExecutionModule2 = new MockTaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode); engineProxy2 = new EngineProxy(taskExecutionModule2, handleId, project.FullFileName, project.FullFileName, engine.LoggingServices, null); }
/// <summary> /// Creates an instance of a MockTask, and returns the objects necessary to exercise /// taskEngine.InitializeTask /// </summary> /// <param name="taskNode"></param> /// <param name="taskEngine"></param> /// <param name="mockTask"></param> /// <param name="itemBucket"></param> /// <owner>RGoel</owner> private void InstantiateMockTaskHelper ( XmlElement taskNode, out TaskEngine taskEngine, out MockTask mockTask, out ItemBucket itemBucket, out EngineProxy engineProxy, string condition ) { LoadedType taskClass = new LoadedType(typeof(MockTask), new AssemblyLoadInfo(typeof(MockTask).Assembly.FullName, null)); Engine engine = new Engine(@"c:\"); Project project = new Project(engine); EngineCallback engineCallback = new EngineCallback(engine); TaskExecutionModule taskExecutionModule = new TaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.SingleProcMode, false); ProjectBuildState buildContext = new ProjectBuildState(null, null, new BuildEventContext(0, 1, 1, 1)); int nodeProxyID = engineCallback.CreateTaskContext(project, null, buildContext, taskNode, EngineCallback.inProcNode, new BuildEventContext(BuildEventContext.InvalidNodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId)); taskEngine = new TaskEngine ( taskNode, null, /* host object */ "In Memory", project.FullFileName, engine.LoggingServices, nodeProxyID, taskExecutionModule, new BuildEventContext(0, 1, 1, 1) ); taskEngine.TaskClass = taskClass; engineProxy = new EngineProxy(taskExecutionModule, nodeProxyID, project.FullFileName, project.FullFileName, engine.LoggingServices, null); mockTask = new MockTask(new EngineProxy(taskExecutionModule, nodeProxyID, project.FullFileName, project.FullFileName, engine.LoggingServices, null)); // The code below creates an item table that is equivalent to the following MSBuild syntax: // // <ItemGroup> // <ItemListContainingOneItem Include="a.cs"> // <Culture>fr-fr</Culture> // </ItemListContainingOneItem> // // <ItemListContainingTwoItems Include="b.cs"> // <HintPath>c:\foo</HintPath> // </ItemListContainingTwoItems> // <ItemListContainingTwoItems Include="c.cs"> // <HintPath>c:\bar</HintPath> // </ItemListContainingTwoItems> // </ItemGroup> // Hashtable itemsByName = new Hashtable(StringComparer.OrdinalIgnoreCase); BuildItemGroup itemListContainingOneItem = new BuildItemGroup(); BuildItem a = itemListContainingOneItem.AddNewItem("ItemListContainingOneItem", "a.cs"); a.SetMetadata("Culture", "fr-fr"); itemsByName["ItemListContainingOneItem"] = itemListContainingOneItem; BuildItemGroup itemListContainingTwoItems = new BuildItemGroup(); BuildItem b = itemListContainingTwoItems.AddNewItem("ItemListContainingTwoItems", "b.cs"); b.SetMetadata("HintPath", "c:\\foo"); BuildItem c = itemListContainingTwoItems.AddNewItem("ItemListContainingTwoItems", "c.cs"); c.SetMetadata("HintPath", "c:\\bar"); itemsByName["ItemListContainingTwoItems"] = itemListContainingTwoItems; itemBucket = new ItemBucket(new string[0], new Dictionary<string, string>(), LookupHelpers.CreateLookup(itemsByName), 0); }
/********************************************************************************* * * Helpers * *********************************************************************************/ private void InstantiateMockTaskHelper ( XmlElement taskNode, out TaskEngine taskEngine, out MockTask mockTask, out ItemBucket itemBucket, out EngineProxy engineProxy ) { InstantiateMockTaskHelper(taskNode, out taskEngine, out mockTask, out itemBucket, out engineProxy, null); }
/// <summary> /// Execute a task object for a given bucket. /// </summary> /// <param name="engineProxy"></param> /// <param name="bucket"></param> /// <param name="howToExecuteTask"></param> /// <param name="task"></param> /// <param name="taskResult">Whether the task returned true from Execute</param> /// <returns>true if task executed successfully (possibly failed but continueOnError=true)</returns> private bool ExecuteInstantiatedTask(EngineProxy engineProxy, ItemBucket bucket, TaskExecutionMode howToExecuteTask, ITask task, out bool taskResult) { UpdateContinueOnError(bucket, engineProxy); taskResult = false; bool taskExecutedSuccessfully = true; if (!InitializeTask(task, bucket, engineProxy)) { // The task cannot be initialized. ProjectErrorUtilities.VerifyThrowInvalidProject(false, taskNode, "TaskParametersError", TaskName, String.Empty); } else { bool taskReturned = false; try { taskResult = task.Execute(); taskReturned = true; } // if a logger has failed, abort immediately catch (LoggerException) { // Polite logger failure throw; } catch (InternalLoggerException) { // Logger threw arbitrary exception throw; } // handle any exception thrown by the task during execution // NOTE: We catch ALL exceptions here, to attempt to completely isolate the Engine // from failures in the task. Probably we should try to avoid catching truly fatal exceptions, // e.g., StackOverflowException catch (Exception e) { if (continueOnError) { loggingServices.LogTaskWarningFromException(buildEventContext, e, // Don't try and log the line/column number for this error if // ContinueOnError=true, because it's too expensive to do so, // and this error may be fairly common and expected. new BuildEventFileInfo(projectFileOfTaskNode), TaskName); // Log a message explaining why we converted the previous error into a warning. loggingServices.LogComment(buildEventContext, MessageImportance.Normal, "ErrorConvertedIntoWarning"); } else { loggingServices.LogFatalTaskError(buildEventContext, e, CreateBuildEventFileInfoForTask(), TaskName); } } // If the task returned attempt to gather its outputs. If gathering outputs fails set the taskResults // to false if (taskReturned) { taskResult = GatherTaskOutputs(howToExecuteTask, task, bucket) && taskResult; } // If the taskResults are false look at ContinueOnError. If ContinueOnError=false (default) // mark the taskExecutedSuccessfully=false. Otherwise let the task succeed but log a normal // pri message that says this task is continuing because ContinueOnError=true if (!taskResult) { if (!continueOnError) { taskExecutedSuccessfully = false; } else { loggingServices.LogComment(buildEventContext, MessageImportance.Normal, "TaskContinuedDueToContinueOnError", "ContinueOnError", TaskName, "true"); } } } return taskExecutedSuccessfully; }
/// <summary> /// Given an instantiated task, this method initializes it, and sets all the task parameters (which are defined as /// properties of the task class). /// </summary> /// <remarks> /// This method is internal for unit-testing purposes only. /// </remarks> /// <returns>true, if successful</returns> internal bool InitializeTask(ITask task, ItemBucket bucket, EngineProxy engineProxy) { try { task.BuildEngine = engineProxy; task.HostObject = hostObject; } // if a logger has failed, abort immediately catch (LoggerException) { // Polite logger failure throw; } catch (InternalLoggerException) { // Logger threw arbitrary exception throw; } // handle any exception thrown by the task during initialization catch (Exception e) { // NOTE: We catch ALL exceptions here, to attempt to completely isolate the Engine // from failures in the task. Probably we should try to avoid catching truly fatal exceptions, // e.g., StackOverflowException loggingServices.LogFatalTaskError(buildEventContext, e, // Display the task's exception stack. // Log the task line number, whatever the value of ContinueOnError; // because InitializeTask failure will be a hard error anyway. CreateBuildEventFileInfoForTask(), TaskName); return false; } bool taskInitialized = InitializeTaskParameters(task, bucket); return taskInitialized; }
/// <summary> /// Recomputes the task's "ContinueOnError" setting. /// </summary> /// <param name="bucket"></param> /// <param name="engineProxy"></param> private void UpdateContinueOnError(ItemBucket bucket, EngineProxy engineProxy) { XmlAttribute continueOnErrorAttribute = taskNode.Attributes[XMakeAttributes.continueOnError]; try { continueOnError = ( // if attribute doesn't exist, default to "false" (continueOnErrorAttribute != null) && // otherwise, convert its value to a boolean ConversionUtilities.ConvertStringToBool ( // expand embedded item vectors after expanding properties and item metadata bucket.Expander.ExpandAllIntoString(continueOnErrorAttribute) ) ); } // handle errors in string-->bool conversion catch (ArgumentException e) { ProjectErrorUtilities.VerifyThrowInvalidProject(false, continueOnErrorAttribute, "InvalidContinueOnErrorAttribute", TaskName, e.Message); } // We need to access an internal method of the EngineProxy in order to update the value // of continueOnError that will be returned to the task when the task queries IBuildEngine for it engineProxy.UpdateContinueOnError(continueOnError); }
/// <summary> /// Execute a single bucket /// </summary> /// <returns>true if execution succeeded</returns> private bool ExecuteBucket(EngineProxy engineProxy, ItemBucket bucket, int bucketNumber, TaskExecutionMode howToExecuteTask) { if ( (this.conditionAttribute != null) && !Utilities.EvaluateCondition(this.conditionAttribute.Value, this.conditionAttribute, bucket.Expander, null, ParserOptions.AllowAll, loggingServices, buildEventContext) ) { // Condition is false if (howToExecuteTask == TaskExecutionMode.ExecuteTaskAndGatherOutputs) { if (!loggingServices.OnlyLogCriticalEvents) { // Expand the expression for the Log. string expanded = bucket.Expander.ExpandAllIntoString(this.conditionAttribute); // Whilst we are within the processing of the task, we haven't actually started executing it, so // our skip task message needs to be in the context of the target. However any errors should be reported // at the point where the task appears in the project. BuildEventContext skipTaskContext = new BuildEventContext(buildEventContext.NodeId, buildEventContext.TargetId, buildEventContext.ProjectContextId, BuildEventContext.InvalidTaskId); loggingServices.LogComment(skipTaskContext, "TaskSkippedFalseCondition", TaskName, this.conditionAttribute.Value, expanded); } } return true; } bool taskExecutedSuccessfully = true; // Condition is true if (howToExecuteTask == TaskExecutionMode.ExecuteTaskAndGatherOutputs) { // Now that we know we will need to execute the task, // Ensure the TaskEngine is initialized with the task class // This does the work of task discovery, if it // hasn't already been done. bool taskClassWasFound = FindTask(); if (!taskClassWasFound) { // Task wasn't discovered, we cannot continue return false; } // Now instantiate, initialize, and execute the task ITask task; // If this is the first bucket use the task context originally given to it, for the remaining buckets get a unique id for them if (bucketNumber != 0) { // Ask the parent engine the next Id which should be used for the taskId. buildEventContext = new BuildEventContext(buildEventContext.NodeId, buildEventContext.TargetId, buildEventContext.ProjectContextId, parentModule.GetNextTaskId()); // For each batch the engineProxy needs to have the correct buildEventContext as all messages comming from a task will have the buildEventContext of the EngineProxy. engineProxy.BuildEventContext = buildEventContext; } loggingServices.LogTaskStarted(buildEventContext, TaskName, parentProjectFullFileName, projectFileOfTaskNode); AppDomain taskAppDomain = PrepareAppDomain(); bool taskResult = false; try { task = InstantiateTask(taskAppDomain); // If task cannot be instantiated, we consider its declaration/usage to be invalid. ProjectErrorUtilities.VerifyThrowInvalidProject(task != null, taskNode, "TaskDeclarationOrUsageError", TaskName); taskExecutedSuccessfully = ExecuteInstantiatedTask(engineProxy, bucket, howToExecuteTask, task, out taskResult); if (lookupHash != null) { List<string> overrideMessages = bucket.Lookup.GetPropertyOverrideMessages(lookupHash); if (overrideMessages != null) { foreach (string s in overrideMessages) { loggingServices.LogCommentFromText(buildEventContext, MessageImportance.Low, s); } } } } catch (InvalidProjectFileException e) { // Make sure the Invalid Project error gets logged *before* TaskFinished. Otherwise, // the log is confusing. loggingServices.LogInvalidProjectFileError(buildEventContext, e); throw; } finally { // Flag the completion of the task. loggingServices.LogTaskFinished( buildEventContext, TaskName, parentProjectFullFileName, projectFileOfTaskNode, taskResult); task = null; if (taskAppDomain != null) { AppDomain.Unload(taskAppDomain); taskAppDomain = null; } } } else { Debug.Assert(howToExecuteTask == TaskExecutionMode.InferOutputsOnly); ErrorUtilities.VerifyThrow(GatherTaskOutputs(howToExecuteTask, null, bucket), "The method GatherTaskOutputs() should never fail when inferring task outputs."); if (lookupHash != null) { List<string> overrideMessages = bucket.Lookup.GetPropertyOverrideMessages(lookupHash); if (overrideMessages != null) { foreach (string s in overrideMessages) { loggingServices.LogCommentFromText(buildEventContext, MessageImportance.Low, s); } } } } return taskExecutedSuccessfully; }
/// <summary> /// Called to execute a task within a target. This method instantiates the task, sets its parameters, and executes it. /// </summary> /// <returns>true, if successful</returns> internal bool ExecuteTask(TaskExecutionMode howToExecuteTask, Lookup lookup) { ErrorUtilities.VerifyThrow(lookup != null, "Need to specify items available to task."); bool taskExecutedSuccessfully = false; EngineProxy engineProxy = null; ArrayList buckets = null; try { engineProxy = new EngineProxy(parentModule, handleId, parentProjectFullFileName, projectFileOfTaskNode, loggingServices, buildEventContext); List<string> taskParameterValues = CreateListOfParameterValues(); buckets = BatchingEngine.PrepareBatchingBuckets(taskNode, taskParameterValues, lookup); lookupHash = null; // Only create a hash table if there are more than one bucket as this is the only time a property can be overridden if (buckets.Count > 1) { lookupHash = Utilities.CreateTableIfNecessary((Hashtable)null); } // Loop through each of the batch buckets and execute them one at a time for (int i=0; i < buckets.Count; i++) { // Execute the batch bucket, pass in which bucket we are executing so that we know when to get a new taskId for the bucket. taskExecutedSuccessfully = ExecuteBucket(engineProxy, (ItemBucket)buckets[i], i, howToExecuteTask); if (!taskExecutedSuccessfully) { break; } } } finally { // Remove the AssemblyResolve handler in the default AppDomain, we are done with the task. if (resolver != null) { resolver.RemoveHandler(); } if (engineProxy != null) { engineProxy.MarkAsInActive(); } // Now all task batches are done, apply all item adds to the outer // target batch; we do this even if the task wasn't found (in that case, // no items or properties will have been added to the scope) if (buckets != null) { foreach (ItemBucket bucket in buckets) { bucket.Lookup.LeaveScope(); } } } return taskExecutedSuccessfully; }