public void ExecutionProperties() { Engine engine = new Engine(); engine.BinPath="TestBinPath"; ArrayList targetsToBuild = new ArrayList(); targetsToBuild.Add("targetName"); ProjectBuildState projectContext = new ProjectBuildState(new BuildRequest(-1, null, null, (BuildPropertyGroup)null, null, -1, false, false), targetsToBuild, new BuildEventContext(0, 1, 1, 1)); TaskExecutionContext context = new TaskExecutionContext(null, null, null, projectContext, 4, EngineCallback.inProcNode, new BuildEventContext(BuildEventContext.InvalidNodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId)); Assert.IsTrue(context.BuildContext.TargetNamesToBuild.Contains("targetName"), "Expected target list to contain targetName"); Assert.AreEqual(EngineCallback.inProcNode,context.NodeIndex); Assert.IsNull(context.ParentTarget,"Expected ParentTarget to be null"); context.SetTaskOutputs(false, null, 0); Assert.IsFalse(context.TaskExecutedSuccessfully ); Assert.IsNull(context.ThrownException, "Expected ThrownException to be null"); context.SetTaskOutputs(true, new Exception(), 0); Assert.IsTrue(context.TaskExecutedSuccessfully); Assert.IsNotNull(context.ThrownException,"Expected ThrownException to not be null"); }
internal TargetInProgessState ( EngineCallback engineCallback, Target target, List <ProjectBuildState> waitingBuildStates, ProjectBuildState initiatingRequest, BuildRequest [] outstandingBuildRequests, string projectName ) { this.targetId = new TargetIdWrapper(target); this.outstandingBuildRequests = outstandingBuildRequests; // For each waiting build context try to find the parent target this.parentBuildRequests = new List <BuildRequest>(); this.parentTargets = new List <TargetIdWrapper>(); this.projectName = projectName; // Process the waiting contexts if there are any if (waitingBuildStates != null) { for (int i = 0; i < waitingBuildStates.Count; i++) { ProcessBuildContext(engineCallback, waitingBuildStates[i], target); } } // Process the initiating context ProcessBuildContext(engineCallback, initiatingRequest, target); }
internal TargetInProgessState ( EngineCallback engineCallback, Target target, List<ProjectBuildState> waitingBuildStates, ProjectBuildState initiatingRequest, BuildRequest [] outstandingBuildRequests, string projectName ) { this.targetId = new TargetIdWrapper(target); this.outstandingBuildRequests = outstandingBuildRequests; // For each waiting build context try to find the parent target this.parentBuildRequests = new List<BuildRequest>(); this.parentTargets = new List<TargetIdWrapper>(); this.projectName = projectName; // Process the waiting contexts if there are any if (waitingBuildStates != null) { for (int i = 0; i < waitingBuildStates.Count; i++) { ProcessBuildContext(engineCallback, waitingBuildStates[i], target); } } // Process the initiating context ProcessBuildContext(engineCallback, initiatingRequest, target); }
/// <summary> /// Find all the build contexts that connects child to parent. The only time multiple contexts are possible /// is if the connection is formed by an IBuildEngine callback which requests the same target in the /// same project to be build in parallel multiple times. /// </summary> internal List <ProjectBuildState> FindConnectingContexts ( TargetInProgessState child, TargetInProgessState parent, Target childTarget, List <ProjectBuildState> waitingStates, ProjectBuildState initiatingBuildContext ) { List <ProjectBuildState> connectingContexts = new List <ProjectBuildState>(); // Since the there is a cycle formed at the child there must be at least two requests // since the edge between the parent and the child is a backward edge ErrorUtilities.VerifyThrow(waitingStates != null, "There must be a at least two requests at the child"); for (int i = 0; i < waitingStates.Count; i++) { if (child.CheckBuildContextForParentMatch(parentEngine.EngineCallback, parent.TargetId, childTarget, waitingStates[i])) { connectingContexts.Add(waitingStates[i]); } } if (child.CheckBuildContextForParentMatch(parentEngine.EngineCallback, parent.TargetId, childTarget, initiatingBuildContext)) { connectingContexts.Add(initiatingBuildContext); } return(connectingContexts); }
/// <summary> /// Given a build state try to find the parent target that caused this build state to /// come into being either via dependent, on error relationship or via IBuildEngine call /// </summary> internal TargetIdWrapper FindParentTarget ( EngineCallback engineCallback, ProjectBuildState buildContext, Target target, out BuildRequest parentRequest ) { // We need to find the parent target parentRequest = null; // Skip build states that have already been filled if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.RequestFilled) { return(null); } // Check if the target was called due to a onerror or depends on call if (buildContext.ContainsBlockingTarget(target.Name)) { // Figure out the record for the parent target Project containingProject = target.ParentProject; Target parentTarget = containingProject.Targets[buildContext.GetParentTarget(target.Name)]; return(new TargetIdWrapper(parentTarget)); } else { // The build context must have formed due to IBuildEngine call ErrorUtilities.VerifyThrow( String.Compare(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), target.Name, StringComparison.OrdinalIgnoreCase) == 0, "The target should be the in progress target for the context"); // This target is called due to IBuildEngine or host request return(FindParentTargetForBuildRequest(engineCallback, buildContext.BuildRequest, out parentRequest)); } }
/// <summary> /// Figure out the parent target or the parent build request for the given context /// </summary> private void ProcessBuildContext(EngineCallback engineCallback, ProjectBuildState buildContext, Target target) { BuildRequest parentRequest = null; TargetIdWrapper parentName = FindParentTarget(engineCallback, buildContext, target, out parentRequest); if (parentName != null) { parentTargets.Add(parentName); } if (parentRequest != null) { parentBuildRequests.Add(parentRequest); } }
/// <summary> /// Figure out the parent target or the parent build request for the given context /// </summary> private void ProcessBuildContext(EngineCallback engineCallback, ProjectBuildState buildContext, Target target) { BuildRequest parentRequest = null; TargetIdWrapper parentName = FindParentTarget(engineCallback, buildContext, target, out parentRequest); if (parentName != null) { parentTargets.Add(parentName); } if (parentRequest != null) { parentBuildRequests.Add(parentRequest); } }
/// <summary> /// Default constructor for creation of task execution wrapper /// </summary> internal TaskExecutionContext ( Project parentProject, Target parentTarget, XmlElement taskNode, ProjectBuildState buildContext, int handleId, int nodeIndex, BuildEventContext taskBuildEventContext ) : base(handleId, nodeIndex, taskBuildEventContext) { this.parentProject = parentProject; this.parentTarget = parentTarget; this.taskNode = taskNode; this.buildContext = buildContext; this.thrownException = null; }
/// <summary> /// Default constructor for creation of task execution wrapper /// </summary> internal TaskExecutionContext ( Project parentProject, Target parentTarget, XmlElement taskNode, ProjectBuildState buildContext, int handleId, int nodeIndex, BuildEventContext taskBuildEventContext ) :base(handleId, nodeIndex, taskBuildEventContext) { this.parentProject = parentProject; this.parentTarget = parentTarget; this.taskNode = taskNode; this.buildContext = buildContext; this.thrownException = null; }
public void TaskExecutionContextCreation() { // Create some items to instantiate a task execution context and check to make sure those values are set properly Engine engine = new Engine(); engine.BinPath="TestBinPath"; ArrayList targetsToBuild = new ArrayList(); targetsToBuild.Add("targetName"); ProjectBuildState projectContext = new ProjectBuildState(null, targetsToBuild, new BuildEventContext(0, 1, 1, 1)); TaskExecutionContext context = new TaskExecutionContext(null, null, null, projectContext, 4, EngineCallback.inProcNode, new BuildEventContext(BuildEventContext.InvalidNodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId)); Assert.IsTrue(context.BuildContext.TargetNamesToBuild.Contains("targetName"),"Expected target list to contain targetName"); Assert.IsNull(context.ParentTarget,"ParentTarget should be null"); Assert.IsNull(context.ThrownException,"ThrownException should be null"); Assert.AreEqual(4,context.HandleId,"Node ProxyId should be 4"); }
/// <summary> /// This method creates a new TaskExecutionContext and return a integer token that maps to it. /// This method is not thread safe and must be called only from the engine thread. /// </summary> internal int CreateTaskContext ( Project parentProject, Target parentTarget, ProjectBuildState buildContext, XmlElement taskNode, int nodeIndex, BuildEventContext taskContext ) { int handleId = nextContextId; nextContextId++; TaskExecutionContext executionContext = new TaskExecutionContext(parentProject, parentTarget, taskNode, buildContext, handleId, nodeIndex, taskContext); executionContexts.Add(handleId, executionContext); return(handleId); }
/// <summary> /// This function checks if the given ProjectBuildState is caused by a given parent target (via /// a dependency, onerror or IBuildEngine relationship) /// </summary> internal bool CheckBuildContextForParentMatch ( EngineCallback engineCallback, TargetIdWrapper parentId, Target target, ProjectBuildState projectBuildState ) { BuildRequest parentRequest = null; TargetInProgessState.TargetIdWrapper parentName = FindParentTarget(engineCallback, projectBuildState, target, out parentRequest); if (parentName != null && parentName.Equals(parentId)) { return(true); } if (parentRequest != null) { for (int j = 0; j < parentBuildRequests.Count; j++) { if (parentRequest.HandleId == parentBuildRequests[j].HandleId && parentRequest.RequestId == parentBuildRequests[j].RequestId) { if (parentTargetsForBuildRequests[j].Equals(parentId)) { return(true); } else { return(false); } } } } return(false); }
/// <summary> /// Given a build state try to find the parent target that caused this build state to /// come into being either via dependent, on error relationship or via IBuildEngine call /// </summary> internal TargetIdWrapper FindParentTarget ( EngineCallback engineCallback, ProjectBuildState buildContext, Target target, out BuildRequest parentRequest ) { // We need to find the parent target parentRequest = null; // Skip build states that have already been filled if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.RequestFilled) { return null; } // Check if the target was called due to a onerror or depends on call if (buildContext.ContainsBlockingTarget(target.Name)) { // Figure out the record for the parent target Project containingProject = target.ParentProject; Target parentTarget = containingProject.Targets[buildContext.GetParentTarget(target.Name)]; return new TargetIdWrapper(parentTarget); } else { // The build context must have formed due to IBuildEngine call ErrorUtilities.VerifyThrow( String.Compare(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), target.Name, StringComparison.OrdinalIgnoreCase) == 0, "The target should be the in progress target for the context"); // This target is called due to IBuildEngine or host request return FindParentTargetForBuildRequest(engineCallback, buildContext.BuildRequest, out parentRequest); } }
private void ContinueRunningTasks ( ProjectBuildState buildContext, TaskExecutionContext taskExecutionContext, bool startingFirstTask ) { bool exitDueToError = true; try { // If this is the first task - initialize for running it if (startingFirstTask) { InitializeForRunningTargetBatches(); } // If run a task then process its outputs if (currentTask != targetElement.ChildNodes.Count && !startingFirstTask) { ProcessTaskOutputs(taskExecutionContext); } // Check if we processed the last node in a batch or terminated the batch due to error if (currentTask == targetElement.ChildNodes.Count || exitBatchDueToError) { FinishRunningSingleTargetBatch(); // On failure transition into unsuccessful state if (!targetBuildSuccessful) { overallSuccess = false; FinishRunningTargetBatches(buildContext); // Transition the state machine into building the error clause state InitializeOnErrorClauseExecution(); inProgressBuildState = InProgressBuildState.BuildingErrorClause; ExecuteErrorTarget(buildContext); exitDueToError = false; return; } //Check if this was the last bucket if (currentBucket == buckets.Count) { FinishRunningTargetBatches(buildContext); inProgressBuildState = InProgressBuildState.NotInProgress; // Notify targets that are waiting for the results NotifyBuildCompletion(Target.BuildState.CompletedSuccessfully, null); exitDueToError = false; return; } // Prepare the next bucket InitializeForRunningSingleTargetBatch(); } // Execute the current task ExecuteCurrentTask(buildContext); exitDueToError = false; } catch (InvalidProjectFileException e) { // Make sure the Invalid Project error gets logged *before* TargetFinished. Otherwise, // the log is confusing. this.parentEngine.LoggingServices.LogInvalidProjectFileError(targetBuildEventContext, e); throw; } finally { if (exitDueToError && loggedTargetStart) { // Log that the target has failed parentEngine.LoggingServices.LogTargetFinished( targetBuildEventContext, targetClass.Name, this.parentProject.FullFileName, targetClass.ProjectFileOfTargetElement, false); } } }
public void TargetInProgressStateCustomSerialization() { Engine engine = new Engine(@"c:\"); Project project = ObjectModelHelpers.CreateInMemoryProject(@" <Project DefaultTargets=`t` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`> <PropertyGroup> <OutputPath>bin\Debug\</OutputPath> <AssemblyName>MyAssembly</AssemblyName> <OutputType>Exe</OutputType> <Configuration>Debug</Configuration> </PropertyGroup> <ItemGroup> <Compile Include=`Class1.cs` /> <EmbeddedResource Include=`Resource1.txt` /> <EmbeddedResource Include=`Resource2.resx` /> </ItemGroup> <Target Name='t' DependsOnTargets='Build'/> <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.Targets` /> </Project> "); EngineCallback engineCallback = new EngineCallback(engine); Target build = project.Targets["Build"]; List<ProjectBuildState> waitingBuildStates = null; int handleId = 1; string projectFileName = "ProjectFileName"; string[] targetNames = new string[] { "t" }; Dictionary<string, string> dictionary = null; int requestId = 1; BuildRequest request = new BuildRequest(handleId, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false); ArrayList targetNamesToBuild = new ArrayList(); targetNamesToBuild.Add("t"); ProjectBuildState initiatingRequest = new ProjectBuildState(request, targetNamesToBuild, new BuildEventContext(1, 2, 2, 2)); initiatingRequest.AddBlockingTarget("Build"); BuildRequest [] outstandingBuildRequests = null; string projectName = "SuperTestProject"; TargetInProgessState targetInProgress1 = new TargetInProgessState( engineCallback, build, waitingBuildStates, initiatingRequest, outstandingBuildRequests, projectName ); targetInProgress1.ParentTargetsForBuildRequests = null; Assertion.AssertNull(targetInProgress1.ParentTargetsForBuildRequests); Assertion.Assert(!targetInProgress1.RequestedByHost); build = project.Targets["t"]; request = new BuildRequest(handleId, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false); request.IsExternalRequest = true; targetNamesToBuild.Add("t"); initiatingRequest = new ProjectBuildState(request, targetNamesToBuild, new BuildEventContext(1, 2, 2, 2)); outstandingBuildRequests = new BuildRequest[]{ new BuildRequest(1, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false), new BuildRequest(2, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false), new BuildRequest(3, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false), new BuildRequest(4, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false) }; TargetInProgessState.TargetIdWrapper originalWrapper = new TargetInProgessState.TargetIdWrapper(); originalWrapper.id = 1; originalWrapper.name = "Wrapper"; originalWrapper.nodeId = 4; originalWrapper.projectId = 6; waitingBuildStates = new List<ProjectBuildState>(); waitingBuildStates.Add(initiatingRequest); TargetInProgessState targetInProgress3 = new TargetInProgessState( engineCallback, build, waitingBuildStates, initiatingRequest, outstandingBuildRequests, projectName ); targetInProgress3.ParentTargetsForBuildRequests = new TargetInProgessState.TargetIdWrapper[] { originalWrapper }; // Stream, writer and reader where the events will be serialized and deserialized from MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); BinaryReader reader = new BinaryReader(stream); try { stream.Position = 0; // Serialize targetInProgress3.WriteToStream(writer); // Get position of stream after write so it can be compared to the position after read long streamWriteEndPosition = stream.Position; // Deserialize and Verify stream.Position = 0; TargetInProgessState newInProgressState = new TargetInProgessState(); newInProgressState.CreateFromStream(reader); long streamReadEndPosition = stream.Position; Assert.IsTrue(string.Compare(newInProgressState.ProjectName, projectName, StringComparison.OrdinalIgnoreCase) == 0); Assert.IsTrue(string.Compare(newInProgressState.TargetId.name, "t", StringComparison.OrdinalIgnoreCase) == 0); Assert.IsNotNull(newInProgressState.ParentTargets); Assert.IsTrue(newInProgressState.OutstandingBuildRequests.Length == 4); Assert.IsNotNull(newInProgressState.ParentBuildRequests); Assert.IsNotNull(newInProgressState.ParentTargetsForBuildRequests.Length == 1); stream.Position = 0; // Serialize targetInProgress1.WriteToStream(writer); // Get position of stream after write so it can be compared to the position after read streamWriteEndPosition = stream.Position; // Deserialize and Verify stream.Position = 0; newInProgressState = new TargetInProgessState(); newInProgressState.CreateFromStream(reader); streamReadEndPosition = stream.Position; Assert.IsTrue(string.Compare(newInProgressState.ProjectName, projectName,StringComparison.OrdinalIgnoreCase)==0); Assert.IsTrue(string.Compare(newInProgressState.TargetId.name,"Build",StringComparison.OrdinalIgnoreCase)==0); Assert.IsNotNull(newInProgressState.ParentTargets); Assert.IsNull(newInProgressState.OutstandingBuildRequests); Assert.IsNotNull(newInProgressState.ParentBuildRequests); TargetInProgessState targetInProgress2 = new TargetInProgessState(); stream.Position = 0; // Serialize targetInProgress2.WriteToStream(writer); // Get position of stream after write so it can be compared to the position after read streamWriteEndPosition = stream.Position; // Deserialize and Verify stream.Position = 0; newInProgressState = new TargetInProgessState(); newInProgressState.CreateFromStream(reader); streamReadEndPosition = stream.Position; Assert.IsNull(newInProgressState.ProjectName); Assert.IsNull(newInProgressState.TargetId); Assert.IsNull(newInProgressState.ParentTargets); Assert.IsNull(newInProgressState.OutstandingBuildRequests); Assert.IsNull(newInProgressState.ParentBuildRequests); } finally { // Close will close the writer/reader and the underlying stream writer.Close(); reader.Close(); reader = null; stream = null; writer = null; } }
/// <summary> /// Create a TaskExecutionState structure which contains all the information necessary /// to execute the task and send this information over to the TEM for task execution /// </summary> internal void SubmitNonIntrinsicTask ( XmlElement taskNode, ITaskHost hostObject, ProjectBuildState buildContext ) { if (!haveRunANonIntrinsicTask) { InitializeForRunningFirstNonIntrinsicTask(); haveRunANonIntrinsicTask = true; } TaskExecutionMode executionMode = DetermineExecutionMode(); // A TaskExecutionMode of ExecuteTaskAndGatherOutputs should have its messages logged in the context of the task and therefore should have a valid taskID // A TaskExecutionMode of InferOutputs or Invalid should have its messages logged in the context of the target and therefore should have an invalid taskID BuildEventContext buildEventContext = PrepareBuildEventContext(executionMode == TaskExecutionMode.ExecuteTaskAndGatherOutputs ? false: true); // Create the task execution context int handleId = parentEngine.EngineCallback.CreateTaskContext(parentProject, targetClass, buildContext, taskNode, EngineCallback.inProcNode, buildEventContext); // Create the task execution state TaskExecutionState taskState = new TaskExecutionState ( executionMode, lookupForInference, lookupForExecution, taskNode, hostObject, projectFileOfTaskNode, parentProject.FullFileName, parentProject.ProjectDirectory, handleId, buildEventContext ); // Send the request for task execution to the node parentEngine.NodeManager.ExecuteTask(taskState); }
/// <summary> /// This method is called repeatedly to execute the target in multi-threaded mode. In single /// threaded mode it is called once and it loops internally until the execution is finished. /// </summary> /// <param name="buildContext">Context within which the target is being executed</param> /// <param name="taskExecutionContext">Result of last execution (multi-threaded only)</param> internal void ContinueBuild(ProjectBuildState buildContext, TaskExecutionContext taskExecutionContext) { executionState.ContinueBuild(buildContext, taskExecutionContext); }
/// <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); }
internal void ContinueBuild ( ProjectBuildState buildContext, TaskExecutionContext taskExecutionContext ) { // Verify that the target is in progress ErrorUtilities.VerifyThrow(inProgressBuildState != InProgressBuildState.NotInProgress, "Not in progress"); bool exitedDueToError = true; try { // In the single threaded mode we want to avoid looping all the way back to the // engine because there is no need for to be interruptable to address // other build requests. Instead we loop inside this function untill the target is // fully built. do { // Transition the state machine appropriatly if (inProgressBuildState == InProgressBuildState.RunningTasks) { ContinueRunningTasks(buildContext, taskExecutionContext, false); } else if (inProgressBuildState == InProgressBuildState.BuildingDependencies) { ContinueBuildingDependencies(buildContext); } else if (inProgressBuildState == InProgressBuildState.StartingBuild) { initiatingBuildContext = buildContext; inProgressBuildState = InProgressBuildState.BuildingDependencies; currentDependentTarget = 0; ExecuteDependentTarget(buildContext); } else if (inProgressBuildState == InProgressBuildState.BuildingErrorClause) { ContinueBuildingErrorClause(buildContext); } // In the single threaded mode we need to pull up the outputs of the previous // step if (parentEngine.Router.SingleThreadedMode && inProgressBuildState == InProgressBuildState.RunningTasks) { taskExecutionContext = parentEngine.GetTaskOutputUpdates(); } } while (parentEngine.Router.SingleThreadedMode && inProgressBuildState == InProgressBuildState.RunningTasks); // Indicate that we exited successfully exitedDueToError = false; } finally { if (exitedDueToError) { inProgressBuildState = InProgressBuildState.NotInProgress; NotifyBuildCompletion(Target.BuildState.CompletedUnsuccessfully, buildContext); } } }
private void ContinueBuildingErrorClause (ProjectBuildState buildContext) { // Verify that the target is in the right state ErrorUtilities.VerifyThrow(inProgressBuildState == InProgressBuildState.BuildingErrorClause, "Wrong state"); // Check if all dependent targets have been evaluated ErrorUtilities.VerifyThrow(currentErrorTarget < onErrorTargets.Count, "No error targets left"); // Verify that the target we were waiting on has completed building string nameErrorTarget = onErrorTargets[currentErrorTarget]; ErrorUtilities.VerifyThrow( parentProject.Targets[nameErrorTarget].TargetBuildState != Target.BuildState.InProgress && parentProject.Targets[nameErrorTarget].TargetBuildState != Target.BuildState.NotStarted || buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.ExceptionThrown, "This target should only be updated once the error target is completed"); if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.ExceptionThrown) { inProgressBuildState = InProgressBuildState.NotInProgress; // Call the parent project to update targets waiting on us NotifyBuildCompletion(Target.BuildState.CompletedUnsuccessfully, buildContext); return; } // We don't care if the target has completed successfully, we simply move on to the next one currentErrorTarget++; ExecuteErrorTarget(buildContext); }
/// <summary> /// Find all the build contexts that connects child to parent. The only time multiple contexts are possible /// is if the connection is formed by an IBuildEngine callback which requests the same target in the /// same project to be build in parallel multiple times. /// </summary> internal List<ProjectBuildState> FindConnectingContexts ( TargetInProgessState child, TargetInProgessState parent, Target childTarget, List<ProjectBuildState> waitingStates, ProjectBuildState initiatingBuildContext ) { List<ProjectBuildState> connectingContexts = new List<ProjectBuildState>(); // Since the there is a cycle formed at the child there must be at least two requests // since the edge between the parent and the child is a backward edge ErrorUtilities.VerifyThrow(waitingStates != null, "There must be a at least two requests at the child"); for (int i = 0; i < waitingStates.Count; i++) { if (child.CheckBuildContextForParentMatch(parentEngine.EngineCallback, parent.TargetId, childTarget, waitingStates[i])) { connectingContexts.Add(waitingStates[i]); } } if (child.CheckBuildContextForParentMatch(parentEngine.EngineCallback, parent.TargetId, childTarget, initiatingBuildContext)) { connectingContexts.Add(initiatingBuildContext); } return connectingContexts; }
/// <summary> /// Iterate over the contexts waiting for the target - triggering updates for each of them since the target /// is complete /// </summary> internal void NotifyWaitingTargets(ProjectBuildState errorContext) { // If there was a failure (either unhandled exception or a cycle) the stack will // not unwind properly (i.e. via ContinueBuild call). Therefore the initiating request // must be notified the target completed if the error occurred in another context if (errorContext != null) { AddWaitingBuildContext(initiatingBuildContext); } // Notify the target within the same project that are waiting for current target // These targets are in the process of either building dependencies or error targets // or part of a sequential build context while (waitingTargets != null && waitingTargets.Count != 0) { //Grab the first context ProjectBuildState buildContext = waitingTargets[0]; waitingTargets.RemoveAt(0); //Don't report any messages within the context in which the error occured. That context //is addressed as the base of the stack if (buildContext == errorContext || buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.RequestFilled) { continue; } parentEngine.Scheduler.NotifyOfUnblockedRequest(buildContext.BuildRequest); ErrorUtilities.VerifyThrow( buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.WaitingForTarget || buildContext == initiatingBuildContext, "This context should be waiting for a target to be evaluated"); if (buildContext.NameOfBlockingTarget == null) { ErrorUtilities.VerifyThrow( String.Compare(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), targetClass.Name, StringComparison.OrdinalIgnoreCase) == 0, "The name of the target in progress is inconsistent with the target being built"); // This target was part of a sequential request so we need to notify the parent project // to start building the next target in the sequence if (Engine.debugMode) { Console.WriteLine("Finished " + buildContext.BuildRequest.ProjectFileName + ":" + targetClass.Name + " for node:" + buildContext.BuildRequest.NodeIndex + " HandleId " + buildContext.BuildRequest.HandleId); } } else { // The target on the waiting list must be waiting for this target to complete due to // a dependent or onerror relationship between targets ErrorUtilities.VerifyThrow( String.Compare(buildContext.NameOfBlockingTarget, targetClass.Name, StringComparison.OrdinalIgnoreCase) == 0, "This target should only be updated once the dependent target is completed"); if (Engine.debugMode) { Console.WriteLine("Finished " + targetClass.Name + " notifying " + EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress)); } } // Post a dummy context to the queue to cause the target to run in this context TaskExecutionContext taskExecutionContext = new TaskExecutionContext(parentProject, null, null, buildContext, EngineCallback.invalidEngineHandle, EngineCallback.inProcNode, null); parentEngine.PostTaskOutputUpdates(taskExecutionContext); } }
/// <summary> /// Add a build context that should get a result of the target once it is finished /// </summary> internal void AddWaitingBuildContext(ProjectBuildState buildContext) { if (waitingTargets == null) { waitingTargets = new List<ProjectBuildState>(); } parentEngine.Scheduler.NotifyOfBlockedRequest(buildContext.BuildRequest); waitingTargets.Add(buildContext); }
private void FinishRunningTargetBatches(ProjectBuildState buildContext) { // first, publish all task outputs to the project level foreach (ItemBucket bucket in buckets) { bucket.Lookup.LeaveScope(); } // and also leave the extra scope we created with the cloned project items projectContent.LeaveScope(); // if all batches of the target build successfully if (overallSuccess) { // then, gather the target outputs // NOTE: it is possible that the target outputs computed at this point will be different from the target outputs // used for dependency analysis, but we assume that's what the user intended GatherTargetOutputs(); // Only contexts which are generated from an MSBuild task could need // the outputs of the target, such contexts have a non-null evaluation // request if (buildContext.BuildRequest.OutputsByTarget != null && buildContext.NameOfBlockingTarget == null) { ErrorUtilities.VerifyThrow( String.Compare(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), targetClass.Name, StringComparison.OrdinalIgnoreCase) == 0, "The name of the target in progress is inconsistent with the target being built"); ErrorUtilities.VerifyThrow(targetOutputItems != null, "If the target built successfully, we must have its outputs."); buildContext.BuildRequest.OutputsByTarget[targetClass.Name] = targetOutputItems.ToArray(); } } }
/// <summary> /// This function checks if the given ProjectBuildState is caused by a given parent target (via /// a dependency, onerror or IBuildEngine relationship) /// </summary> internal bool CheckBuildContextForParentMatch ( EngineCallback engineCallback, TargetIdWrapper parentId, Target target, ProjectBuildState projectBuildState ) { BuildRequest parentRequest = null; TargetInProgessState.TargetIdWrapper parentName = FindParentTarget(engineCallback, projectBuildState, target, out parentRequest); if (parentName != null && parentName.Equals(parentId)) { return true; } if (parentRequest != null) { for (int j = 0; j < parentBuildRequests.Count; j++) { if (parentRequest.HandleId == parentBuildRequests[j].HandleId && parentRequest.RequestId == parentBuildRequests[j].RequestId) { if (parentTargetsForBuildRequests[j].Equals(parentId)) { return true; } else { return false; } } } } return false; }
/// <summary> /// Mark the target data structures and notify waiting targets since the target has completed /// </summary> internal void NotifyBuildCompletion ( Target.BuildState stateOfBuild, ProjectBuildState errorContext ) { targetClass.UpdateTargetStateOnBuildCompletion(stateOfBuild, targetOutputItems); if (initiatingBuildContext.NameOfBlockingTarget == null) { initiatingBuildContext.BuildRequest.ResultByTarget[targetClass.Name] = stateOfBuild; } if (!parentEngine.Router.SingleThreadedMode) { // Notify targets that have been waiting on the execution NotifyWaitingTargets(errorContext); } }
private void ExecuteDependentTarget ( ProjectBuildState buildContext ) { if (currentDependentTarget < dependsOnTargetNames.Count) { // Get the Target object for the dependent target. string nameDependentTarget = dependsOnTargetNames[currentDependentTarget]; Target targetToBuild = parentProject.Targets[nameDependentTarget]; // If we couldn't find the dependent Target object, we have a problem. ProjectErrorUtilities.VerifyThrowInvalidProject(targetToBuild != null, targetClass.DependsOnTargetsAttribute, "TargetDoesNotExist", nameDependentTarget); // Update the name of the blocking target buildContext.AddBlockingTarget(nameDependentTarget); } else { // We completed building the dependencies so we need to start running the tasks dependsOnTargetNames = null; inProgressBuildState = InProgressBuildState.RunningTasks; ContinueRunningTasks(buildContext, null, true); } }
private void FinishBuildProjectInProgress(BuildRequest buildRequest, ProjectBuildState buildContext, bool exitedDueToError) { if (buildRequest != null && ProfileBuild) { buildRequest.ProcessingTotalTime += DateTime.Now.Ticks - buildRequest.ProcessingStartTime; } if (buildRequest != null && buildRequest.BuildCompleted || buildContext != null && buildContext.BuildComplete ) { DecrementProjectsInProgress(); } if (exitedDueToError) { SetEngineAbortTo(true); } }
private void ContinueBuildingDependencies (ProjectBuildState buildContext) { // Verify that the target is in the right state ErrorUtilities.VerifyThrow(inProgressBuildState == InProgressBuildState.BuildingDependencies, "Wrong state"); // Check if all dependent targets have been evaluated ErrorUtilities.VerifyThrow(currentDependentTarget < dependsOnTargetNames.Count, "No dependent targets left"); // Verify that the target we were waiting on has completed building string nameDependentTarget = dependsOnTargetNames[currentDependentTarget]; ErrorUtilities.VerifyThrow( parentProject.Targets[nameDependentTarget].TargetBuildState != Target.BuildState.InProgress && parentProject.Targets[nameDependentTarget].TargetBuildState != Target.BuildState.NotStarted || buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.ExceptionThrown, "This target should only be updated once the dependent target is completed"); if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.ExceptionThrown) { inProgressBuildState = InProgressBuildState.NotInProgress; // Call the parent project to update targets waiting on us NotifyBuildCompletion(Target.BuildState.CompletedUnsuccessfully, buildContext); return; } // If the dependent target failed to build we need to execute the onerrorclause (if there is one) // or mark this target as failed (if there is not an error clause) else if (parentProject.Targets[nameDependentTarget].TargetBuildState == Target.BuildState.CompletedUnsuccessfully) { // Transition the state machine into building the error clause state InitializeOnErrorClauseExecution(); inProgressBuildState = InProgressBuildState.BuildingErrorClause; ExecuteErrorTarget(buildContext); return; } // Now that the previous dependent target has been build we need to move to the next dependent target if // there is one currentDependentTarget++; // Execute the current target or transition to a different state if necessary ExecuteDependentTarget(buildContext); }
/// <summary> /// Builds this target if it has not already been built as part of its parent project. Before we actually execute the /// tasks for this target, though, we first call on all the dependent targets to build themselves. /// This function may throw InvalidProjectFileException /// </summary> internal void Build ( ProjectBuildState buildContext ) { // Depending on the build state, we may do different things. switch (buildState) { case BuildState.InProgress: // In single proc mode if the build state was already "in progress" // and somebody just told us to build ourselves, it means that there is // a loop (circular dependency) in the target dependency graph. In multi // proc mode we need to analyze the dependency graph before we can // tell if there a circular dependency or if two independent chains // of targets happen to need the result of this target. if (parentEngine.Router.SingleThreadedMode || buildContext.ContainsCycle(this.Name)) { ProjectErrorUtilities.VerifyThrowInvalidProject(false, TargetElement, "CircularDependency", targetName); } else { buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.WaitingForTarget; this.executionState.AddWaitingBuildContext(buildContext); } break; case BuildState.CompletedSuccessfully: case BuildState.CompletedUnsuccessfully: // If this target has already been built as part of this project, // we're not going to build it again. Just return the result // from when it was built previously. Note: This condition // could really only ever hold true if the user specifically // requested us to build multiple targets and there existed // a direct or indirect dependency relationship between two or // more of those top-level targets. // Note: we aren't really entering the target in question here, so don't use the target // event context. Using the target ID for skipped messages would force us to // cache the individual target IDs for unloaded projects and it's not really worth the trouble. // Just use the parent event context. parentEngine.LoggingServices.LogComment(buildContext.ProjectBuildEventContext, ((buildState == BuildState.CompletedSuccessfully) ? "TargetAlreadyCompleteSuccess" : "TargetAlreadyCompleteFailure"), this.targetName); // Only contexts which are generated from an MSBuild task could need // the outputs of this target, such contexts have a non-null evaluation // request if ((buildState == BuildState.CompletedSuccessfully) && (buildContext.BuildRequest.OutputsByTarget != null && buildContext.NameOfBlockingTarget == null)) { error.VerifyThrow( String.Compare(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), this.Name, StringComparison.OrdinalIgnoreCase) == 0, "The name of the target in progress is inconsistent with the target being built"); error.VerifyThrow(targetOutputItems != null, "If the target built successfully, we must have its outputs."); buildContext.BuildRequest.OutputsByTarget[Name] = targetOutputItems.ToArray(); } if (buildContext.NameOfBlockingTarget == null) { buildContext.BuildRequest.ResultByTarget[Name] = buildState; } break; case BuildState.NotStarted: case BuildState.Skipped: { // Always have to create a new context in build as other projects or targets may try and build this target BuildEventContext targetBuildEventContext = new BuildEventContext ( buildContext.ProjectBuildEventContext.NodeId, this.id, buildContext.ProjectBuildEventContext.ProjectContextId, buildContext.ProjectBuildEventContext.TaskId ); Expander expander = new Expander(this.parentProject.evaluatedProperties, this.parentProject.evaluatedItemsByName); // We first make sure no batching was attempted with the target's condition. if (!conditionCheckedForInvalidMetadataReferences) { if (ExpressionShredder.ContainsMetadataExpressionOutsideTransform(this.Condition)) { ProjectErrorUtilities.ThrowInvalidProject(this.conditionAttribute, "TargetConditionHasInvalidMetadataReference", targetName, this.Condition); } conditionCheckedForInvalidMetadataReferences = true; } // If condition is false (based on propertyBag), set this target's state to // "Skipped" since we won't actually build it. if (!Utilities.EvaluateCondition(this.Condition, this.conditionAttribute, expander, null, ParserOptions.AllowProperties | ParserOptions.AllowItemLists, parentEngine.LoggingServices, targetBuildEventContext)) { buildState = BuildState.Skipped; if (buildContext.NameOfBlockingTarget == null) { buildContext.BuildRequest.ResultByTarget[Name] = buildState; } if (!parentEngine.LoggingServices.OnlyLogCriticalEvents) { // Expand the expression for the Log. string expanded = expander.ExpandAllIntoString(this.Condition, this.conditionAttribute); // By design: Not building dependencies. This is what NAnt does too. parentEngine.LoggingServices.LogComment(targetBuildEventContext, "TargetSkippedFalseCondition", this.targetName, this.Condition, expanded); } } else { // This target has not been built yet. So build it! // Change our state to "in progress". TargetParameters will need to be re-calculated if Inputs and Outputs attribute has changed. buildState = BuildState.InProgress; List <string> batchableTargetParameters = GetBatchableTargetParameters(); executionState = new TargetExecutionWrapper(this, taskElementList, batchableTargetParameters, targetElement, expander, targetBuildEventContext); ContinueBuild(buildContext, null); } } break; default: error.VerifyThrow(false, "Build state {0} not handled in Target.Build method", buildState); break; } }
/// <summary> /// Builds this target if it has not already been built as part of its parent project. Before we actually execute the /// tasks for this target, though, we first call on all the dependent targets to build themselves. /// This function may throw InvalidProjectFileException /// </summary> internal void Build ( ProjectBuildState buildContext ) { // Depending on the build state, we may do different things. switch (buildState) { case BuildState.InProgress: // In single proc mode if the build state was already "in progress" // and somebody just told us to build ourselves, it means that there is // a loop (circular dependency) in the target dependency graph. In multi // proc mode we need to analyze the dependency graph before we can // tell if there a circular dependency or if two independent chains // of targets happen to need the result of this target. if (parentEngine.Router.SingleThreadedMode || buildContext.ContainsCycle(this.Name)) { ProjectErrorUtilities.VerifyThrowInvalidProject(false, TargetElement, "CircularDependency", targetName); } else { buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.WaitingForTarget; this.executionState.AddWaitingBuildContext(buildContext); } break; case BuildState.CompletedSuccessfully: case BuildState.CompletedUnsuccessfully: // If this target has already been built as part of this project, // we're not going to build it again. Just return the result // from when it was built previously. Note: This condition // could really only ever hold true if the user specifically // requested us to build multiple targets and there existed // a direct or indirect dependency relationship between two or // more of those top-level targets. // Note: we aren't really entering the target in question here, so don't use the target // event context. Using the target ID for skipped messages would force us to // cache the individual target IDs for unloaded projects and it's not really worth the trouble. // Just use the parent event context. parentEngine.LoggingServices.LogComment(buildContext.ProjectBuildEventContext, ((buildState == BuildState.CompletedSuccessfully) ? "TargetAlreadyCompleteSuccess" : "TargetAlreadyCompleteFailure"), this.targetName); // Only contexts which are generated from an MSBuild task could need // the outputs of this target, such contexts have a non-null evaluation // request if ((buildState == BuildState.CompletedSuccessfully) && (buildContext.BuildRequest.OutputsByTarget != null && buildContext.NameOfBlockingTarget == null)) { error.VerifyThrow( String.Compare(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), this.Name, StringComparison.OrdinalIgnoreCase) == 0, "The name of the target in progress is inconsistent with the target being built"); error.VerifyThrow(targetOutputItems != null, "If the target built successfully, we must have its outputs."); buildContext.BuildRequest.OutputsByTarget[Name] = targetOutputItems.ToArray(); } if (buildContext.NameOfBlockingTarget == null) { buildContext.BuildRequest.ResultByTarget[Name] = buildState; } break; case BuildState.NotStarted: case BuildState.Skipped: { // Always have to create a new context in build as other projects or targets may try and build this target BuildEventContext targetBuildEventContext = new BuildEventContext ( buildContext.ProjectBuildEventContext.NodeId, this.id, buildContext.ProjectBuildEventContext.ProjectContextId, buildContext.ProjectBuildEventContext.TaskId ); Expander expander = new Expander(this.parentProject.evaluatedProperties, this.parentProject.evaluatedItemsByName); // We first make sure no batching was attempted with the target's condition. if (!conditionCheckedForInvalidMetadataReferences) { if (ExpressionShredder.ContainsMetadataExpressionOutsideTransform(this.Condition)) { ProjectErrorUtilities.ThrowInvalidProject(this.conditionAttribute, "TargetConditionHasInvalidMetadataReference", targetName, this.Condition); } conditionCheckedForInvalidMetadataReferences = true; } // If condition is false (based on propertyBag), set this target's state to // "Skipped" since we won't actually build it. if (!Utilities.EvaluateCondition(this.Condition, this.conditionAttribute, expander, null, ParserOptions.AllowProperties | ParserOptions.AllowItemLists, parentEngine.LoggingServices, targetBuildEventContext)) { buildState = BuildState.Skipped; if (buildContext.NameOfBlockingTarget == null) { buildContext.BuildRequest.ResultByTarget[Name] = buildState; } if (!parentEngine.LoggingServices.OnlyLogCriticalEvents) { // Expand the expression for the Log. string expanded = expander.ExpandAllIntoString(this.Condition, this.conditionAttribute); // By design: Not building dependencies. This is what NAnt does too. parentEngine.LoggingServices.LogComment(targetBuildEventContext, "TargetSkippedFalseCondition", this.targetName, this.Condition, expanded); } } else { // This target has not been built yet. So build it! // Change our state to "in progress". TargetParameters will need to be re-calculated if Inputs and Outputs attribute has changed. buildState = BuildState.InProgress; List<string> batchableTargetParameters = GetBatchableTargetParameters(); executionState = new TargetExecutionWrapper(this, taskElementList, batchableTargetParameters, targetElement, expander, targetBuildEventContext); ContinueBuild(buildContext, null); } } break; default: error.VerifyThrow(false, "Build state {0} not handled in Target.Build method", buildState); break; } }
internal void ContinueBuild(ProjectBuildState buildContext, TaskExecutionContext taskExecutionContext) { if (Engine.debugMode) { Console.WriteLine("Project continue build :" + buildContext.BuildRequest.ProjectFileName + " Handle " + buildContext.BuildRequest.HandleId + " State " + buildContext.CurrentBuildContextState + " current target " + buildContext.NameOfTargetInProgress + " blocking target " + buildContext.NameOfBlockingTarget); } bool exitedDueToError = true; try { if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.BuildingCurrentTarget) { // Execute the next appropriate operation for this target ErrorUtilities.VerifyThrow( taskExecutionContext != null, "Task context should be non-null"); taskExecutionContext.ParentTarget.ContinueBuild(taskExecutionContext.BuildContext, taskExecutionContext); } else if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.StartingFirstTarget) { // Start the first target of the build request buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.BuildingCurrentTarget; GetTargetForName(buildContext.NameOfTargetInProgress).Build(buildContext); } else if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.CycleDetected) { ErrorUtilities.VerifyThrow( taskExecutionContext != null && taskExecutionContext.ParentTarget != null, "Unexpected task context. Should not be null"); // Check that the target is in progress ErrorUtilities.VerifyThrow( taskExecutionContext.ParentTarget.TargetBuildState == Target.BuildState.InProgress, "The target forming the cycle should not be complete"); // Throw invalid project exeception ProjectErrorUtilities.VerifyThrowInvalidProject (false, taskExecutionContext.ParentTarget.TargetElement, "CircularDependency", taskExecutionContext.ParentTarget.Name); } CalculateNextActionForProjectContext(buildContext); exitedDueToError = false; if (Engine.debugMode) { Console.WriteLine("Project after continue build :" + buildContext.BuildRequest.ProjectFileName + " Handle " + buildContext.BuildRequest.HandleId + " State " + buildContext.CurrentBuildContextState + " current target " + buildContext.NameOfTargetInProgress + " blocking target " + buildContext.NameOfBlockingTarget); } } catch (InvalidProjectFileException e) { // Make sure the Invalid Project error gets logged *before* ProjectFinished. Otherwise, // the log is confusing. this.ParentEngine.LoggingServices.LogInvalidProjectFileError(buildContext.ProjectBuildEventContext, e); } finally { if ( (exitedDueToError || buildContext.BuildComplete) && buildContext.CurrentBuildContextState != ProjectBuildState.BuildContextState.RequestFilled) { // If the target that threw an exception is being built due to an // dependson or onerror relationship, it is necessary to make sure // the buildrequests waiting on targets below it get notified of the failure. In single // threaded mode there is only a single outstanding request so this issue is avoided. if (exitedDueToError) { buildContext.RecordBuildException(); if (buildContext.NameOfBlockingTarget != null) { while (buildContext.NameOfBlockingTarget != null) { Target blockingTarget = GetTargetForName(buildContext.NameOfBlockingTarget); if (blockingTarget.ExecutionState != null && blockingTarget.ExecutionState.BuildingRequiredTargets) { blockingTarget.ContinueBuild(buildContext, null); } buildContext.RemoveBlockingTarget(); } Target inprogressTarget = GetTargetForName(buildContext.NameOfTargetInProgress); if (inprogressTarget.ExecutionState != null && inprogressTarget.ExecutionState.BuildingRequiredTargets) { inprogressTarget.ContinueBuild(buildContext, null); } } buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.BuildComplete; } this.buildingCount--; if (buildContext.BuildRequest.FireProjectStartedFinishedEvents) { ParentEngine.LoggingServices.LogProjectFinished(buildContext.ProjectBuildEventContext, FullFileName, buildContext.BuildResult); } // Notify targets in other projects that are waiting on us via IBuildEngine // interface (via MSBuild and CallTarget tasks). if (buildContext.BuildRequest.IsGeneratedRequest) { if (Engine.debugMode) { Console.WriteLine("Notifying about " + buildContext.BuildRequest.ProjectFileName + " about " + buildContext.TargetNamesToBuild[0] + " on node " + buildContext.BuildRequest.NodeIndex + " HandleId " + buildContext.BuildRequest.HandleId + " ReqID " + buildContext.BuildRequest.RequestId); } ParentEngine.Router.PostDoneNotice(buildContext.BuildRequest); } // Don't try to unload projects loaded by the host if (this.buildingCount == 0 && this.needToUnloadProject && !this.IsLoadedByHost) { parentEngine.UnloadProject(this, false /* unload only this project version */); } buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.RequestFilled; } } }
/// <summary> /// Executes all tasks in the target linearly from beginning to end, for one batch of the target. /// </summary> private void ExecuteCurrentTask(ProjectBuildState buildContext) { // Check if this is an empty target if (currentTask == targetElement.ChildNodes.Count) { // This is an empty target so we should transition into completed state ContinueRunningTasks(buildContext, null, false); return; } // Get the current child nodes of the <Target> element. XmlNode targetChildNode = targetElement.ChildNodes[currentTask]; // Handle XML comments under the <target> node (just ignore them) and // also skip OnError tags because they are processed separately and later. // Also evaluate any intrinsic tasks immediately and continue. while ((targetChildNode.NodeType == XmlNodeType.Comment) || (targetChildNode.NodeType == XmlNodeType.Whitespace) || (targetChildNode.Name == XMakeElements.onError) || (IntrinsicTask.IsIntrinsicTaskName(targetChildNode.Name))) { if (IntrinsicTask.IsIntrinsicTaskName(targetChildNode.Name)) { ExecuteIntrinsicTask((XmlElement)targetChildNode); } else { skippedNodeCount++; } currentTask++; // Check if this was the last task in the target if (currentTask == targetElement.ChildNodes.Count) { // Transition into appropriate state ContinueRunningTasks(buildContext, null, false); return; } targetChildNode = targetElement.ChildNodes[currentTask]; } // Any child node other than a task element or <OnError> is not supported. ProjectXmlUtilities.VerifyThrowProjectXmlElementChild(targetChildNode); // Make <ItemDefinitionGroup> illegal inside targets, so we can possibly allow it in future. ProjectErrorUtilities.VerifyThrowInvalidProject(!String.Equals(targetChildNode.Name, XMakeElements.itemDefinitionGroup, StringComparison.Ordinal), targetElement, "ItemDefinitionGroupNotLegalInsideTarget", targetChildNode.Name, XMakeElements.target); ErrorUtilities.VerifyThrow(taskElementList.Count > (currentTask - skippedNodeCount), "The TaskElementCollection in this target doesn't have the same number of BuildTask objects as the number of actual task elements."); // Send the task for execution SubmitNonIntrinsicTask( (XmlElement)targetChildNode, ((BuildTask)taskElementList[(currentTask - skippedNodeCount)]).HostObject, buildContext); return; }
/// <summary> /// This method creates a new TaskExecutionContext and return a integer token that maps to it. /// This method is not thread safe and must be called only from the engine thread. /// </summary> internal int CreateTaskContext ( Project parentProject, Target parentTarget, ProjectBuildState buildContext, XmlElement taskNode, int nodeIndex, BuildEventContext taskContext ) { int handleId = nextContextId; nextContextId = nextContextId + 1; TaskExecutionContext executionContext = new TaskExecutionContext(parentProject, parentTarget, taskNode, buildContext, handleId, nodeIndex, taskContext); executionContexts.Add(handleId, executionContext); return handleId; }
internal void CalculateNextActionForProjectContext(ProjectBuildState buildContext) { // If the build request has been already complete if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.RequestFilled) { return; } // In case the first step of the target failed, the target is empty or needs another target // to be build, it is necessary to recalculate the next action. The loop below is broken as // soon as a target completes or a target requests a task to be executed. bool recalculateAction = true; while (recalculateAction) { recalculateAction = false; // Check if there is a dependent target Target currentTarget = null; if (buildContext.NameOfBlockingTarget != null) { currentTarget = GetTargetForName(buildContext.NameOfBlockingTarget); if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.StartingBlockingTarget) { buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.BuildingCurrentTarget; ExecuteNextActionForProjectContext(buildContext, true); recalculateAction = true; } else if (currentTarget.TargetBuildState != Target.BuildState.InProgress) { if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.WaitingForTarget) { // Get target outputs before moving to the next target currentTarget.Build(buildContext); } buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.BuildingCurrentTarget; buildContext.RemoveBlockingTarget(); ExecuteNextActionForProjectContext(buildContext, false); recalculateAction = true; } } else { currentTarget = GetTargetForName(buildContext.NameOfTargetInProgress); if (currentTarget.TargetBuildState != Target.BuildState.InProgress) { if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.WaitingForTarget) { // Get target outputs before moving to the next target currentTarget.Build(buildContext); buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.BuildingCurrentTarget; } if (currentTarget.TargetBuildState == Target.BuildState.CompletedUnsuccessfully) { // Abort the request and notify everyone buildContext.RecordBuildCompletion(false); } else { // Check if there are no more targets to run if (buildContext.GetNextTarget() == null) { // The request is complete buildContext.RecordBuildCompletion(true); } else { // Move to the next target in the request ExecuteNextActionForProjectContext(buildContext, true); recalculateAction = true; } } } } } }
/// <summary> /// This method is called repeatedly to execute the target in multi-threaded mode. In single /// threaded mode it is called once and it loops internally until the execution is finished. /// </summary> /// <param name="buildContext">Context within which the target is being executed</param> /// <param name="taskExecutionContext">Result of last execution (multi-threaded only)</param> internal void ContinueBuild( ProjectBuildState buildContext, TaskExecutionContext taskExecutionContext) { executionState.ContinueBuild(buildContext, taskExecutionContext); }
private void ExecuteNextActionForProjectContext(ProjectBuildState buildContext, bool initialCall) { Target nextTarget = null; if (buildContext.NameOfBlockingTarget != null) { // Notify the next target in depends on/on error stack nextTarget = GetTargetForName(buildContext.NameOfBlockingTarget); } else { nextTarget = GetTargetForName(buildContext.NameOfTargetInProgress); } // Build the target. Note that this could throw an InvalidProjectFileException, in which // case we want to make sure and still log the ProjectFinished event with completedSuccessfully=false. if (initialCall) { nextTarget.Build(buildContext); } else { nextTarget.ContinueBuild(buildContext, null); } }
private ProjectBuildState InitializeForBuildingTargets(BuildRequest buildRequest) { ProjectBuildState buildContext = null; string[] targetNamesToBuild = buildRequest.TargetNames; // Initialize to the parent requests project context id int projectContextId = buildRequest.ParentBuildEventContext.ProjectContextId; BuildEventContext buildEventContext = null; // Determine if a project started event is required to be fired, if so we may need a new projectContextId if (buildRequest.FireProjectStartedFinishedEvents) { //If we have not already used the context from the project yet, lets use that as our first event context if (!haveUsedInitialProjectContextId) { buildEventContext = projectBuildEventContext; haveUsedInitialProjectContextId = true; } else // We are going to need a new Project context Id and a new buildEventContext { projectContextId = parentEngine.GetNextProjectId(); } } if (buildEventContext == null) { buildEventContext = new BuildEventContext ( projectBuildEventContext.NodeId, projectBuildEventContext.TargetId, projectContextId, projectBuildEventContext.TaskId ); } bool exitedDueToError = true; try { // Refreshing (reevaluating) a project may end up calling ResetBuildStatus which will mark // IsReset=true. This is legitimate because when a project is being reevaluated, we want // to be explicit in saying that any targets that had run previously are no longer valid, // and we must rebuild them on the next build. this.RefreshProjectIfDirty(); // Only log the project started event after making sure the project is reevaluated if necessary, // otherwise we could log stale item/property information. if (!ParentEngine.LoggingServices.OnlyLogCriticalEvents && buildRequest.FireProjectStartedFinishedEvents) { string joinedTargetNamesToBuild = null; if (targetNamesToBuild != null && targetNamesToBuild.Length > 0) { joinedTargetNamesToBuild = EscapingUtilities.UnescapeAll(String.Join(";", targetNamesToBuild)); } // Flag the start of the project build. // // This also passes all the current properties/items and their values. The logger might want to use the // object it gets from this event to see updated property/item values later in the build: so be // careful to use the original "evaluatedProperties" and "evaluatedItems" table, not the clone // "EvaluatedProperties" or "EvaluatedItems" table. It's fine to pass these live tables, because we're // wrapping them in read-only proxies. BuildPropertyGroup propertyGroupForStartedEvent = this.evaluatedProperties; // If we are on the child process we need to fiure out which properties we need to serialize if (ParentEngine.Router.ChildMode) { // Initially set it to empty so that we do not serialize all properties if we are on a child node propertyGroupForStartedEvent = new BuildPropertyGroup(); // Get the list of properties to serialize to the parent node string[] propertyListToSerialize = parentEngine.PropertyListToSerialize; if (propertyListToSerialize != null && propertyListToSerialize.Length > 0) { foreach (string propertyToGet in propertyListToSerialize) { BuildProperty property = this.evaluatedProperties[propertyToGet]; //property can be null if propertyToGet does not exist if (property != null) { propertyGroupForStartedEvent.SetProperty(property); } } } } BuildPropertyGroupProxy propertiesProxy = new BuildPropertyGroupProxy(propertyGroupForStartedEvent); BuildItemGroupProxy itemsProxy = new BuildItemGroupProxy(this.evaluatedItems); ParentEngine.LoggingServices.LogProjectStarted(this.projectId, buildRequest.ParentBuildEventContext, buildEventContext, FullFileName, joinedTargetNamesToBuild, propertiesProxy, itemsProxy); // See comment on DefaultToolsVersion setter. if (treatinghigherToolsVersionsAs40) { ParentEngine.LoggingServices.LogComment(buildEventContext, MessageImportance.High, "TreatingHigherToolsVersionAs40", DefaultToolsVersion); } ParentEngine.LoggingServices.LogComment(buildEventContext, MessageImportance.Low, "ToolsVersionInEffectForBuild", ToolsVersion); } // Incrementing the building count. A single project may be building more than once at a time // because of callbacks by the MSBuild task. this.buildingCount++; // Because we are about to build some targets, we are no longer going to be in the "reset" // state. this.IsReset = false; // This is an ArrayList of strings, where each string is the name of a target that // we need to build. We start out by populating it with the list of targets specified // in the "InitialTargets" attribute of the <Project> node. ArrayList completeListOfTargetNamesToBuild = this.CombinedInitialTargetNames; if (buildRequest.UseResultsCache) { buildRequest.InitialTargets = string.Join(";", (string[])completeListOfTargetNamesToBuild.ToArray(typeof(string))); buildRequest.DefaultTargets = (this.DefaultBuildTargets != null) ? string.Join(";", this.DefaultBuildTargets) : string.Empty; buildRequest.ProjectId = this.projectId; } // If no targets were passed in, use the "defaultTargets" from the // project file. if ((targetNamesToBuild == null) || (targetNamesToBuild.Length == 0)) { string[] defaultTargetsToBuild = this.DefaultBuildTargets; // There wasn't at least one target in the project, then we have // a problem. ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(defaultTargetsToBuild != null, new BuildEventFileInfo(this.FullFileName), "NoTargetSpecified"); completeListOfTargetNamesToBuild.AddRange(defaultTargetsToBuild); } else { completeListOfTargetNamesToBuild.AddRange(targetNamesToBuild); } // Check if the client requests that the project should be unloaded once it is completed needToUnloadProject = buildRequest.UnloadProjectsOnCompletion || needToUnloadProject; buildContext = new ProjectBuildState(buildRequest, completeListOfTargetNamesToBuild, buildEventContext); exitedDueToError = false; } catch (InvalidProjectFileException e) { // Make sure the Invalid Project error gets logged *before* ProjectFinished. Otherwise, // the log is confusing. this.ParentEngine.LoggingServices.LogInvalidProjectFileError(buildEventContext, e); // Mark the build request as completed and failed. The build context is either not created // or discarded buildRequest.BuildCompleted = true; buildRequest.BuildSucceeded = false; buildContext = null; } finally { if (exitedDueToError) { this.buildingCount--; if (buildRequest.FireProjectStartedFinishedEvents) { ParentEngine.LoggingServices.LogProjectFinished( buildEventContext, FullFileName, false); } } } return buildContext; }
private void ExecuteErrorTarget ( ProjectBuildState buildContext ) { if (onErrorTargets != null && currentErrorTarget < onErrorTargets.Count) { // Get the Target object for the dependent target. string nameErrorTarget = onErrorTargets[currentErrorTarget]; Target targetToBuild = parentProject.Targets[nameErrorTarget]; // If we couldn't find the on error Target object, we have a problem. ProjectErrorUtilities.VerifyThrowInvalidProject(targetToBuild != null, targetElement, "TargetDoesNotExist", nameErrorTarget); // Update the name of the blocking target buildContext.AddBlockingTarget(nameErrorTarget); } else { // We completed building the error targets so this target is now failed and we have no more work to do onErrorTargets = null; inProgressBuildState = InProgressBuildState.NotInProgress; // Call the parent project to update targets waiting on us NotifyBuildCompletion(Target.BuildState.CompletedUnsuccessfully, null); } }