/// <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> /// Update the target data structures since the target has completed /// </summary> internal void UpdateTargetStateOnBuildCompletion ( BuildState stateOfBuild, List<BuildItem> targetOutputItemList ) { this.buildState = stateOfBuild; this.targetOutputItems = targetOutputItemList; // Clear the execution state since the build is completed executionState = null; }