/// <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); }
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> /// 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> /// 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> /// 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); }
/// <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> /// 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> /// 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; } }