/// <summary> /// Pushes the list of targets specified onto the target stack in reverse order specified, so that /// they will be built in the order specified. /// </summary> /// <param name="targets">List of targets to build.</param> /// <param name="parentTargetEntry">The target which should be considered the parent of these targets.</param> /// <param name="baseLookup">The lookup to be used to build these targets.</param> /// <param name="addAsErrorTarget">True if this should be considered an error target.</param> /// <param name="stopProcessingOnCompletion">True if target stack processing should terminate when the last target in the list is processed.</param> /// <returns>True if we actually pushed any targets, false otherwise.</returns> private async Task<bool> PushTargets(IList<TargetSpecification> targets, TargetEntry parentTargetEntry, Lookup baseLookup, bool addAsErrorTarget, bool stopProcessingOnCompletion, TargetPushType pushType) { List<TargetEntry> targetsToPush = new List<TargetEntry>(targets.Count); // Iterate the list in reverse order so that the first target in the list is the last pushed, and thus the first to be executed. for (int i = targets.Count - 1; i >= 0; i--) { TargetSpecification targetSpecification = targets[i]; if (pushType != TargetPushType.Normal) { // Don't build any Before or After targets for which we already have results. Unlike other targets, // we don't explicitly log a skipped-with-results message because it is not interesting. if (_buildResult.HasResultsForTarget(targetSpecification.TargetName)) { if (_buildResult[targetSpecification.TargetName].ResultCode != TargetResultCode.Skipped) { continue; } } } ElementLocation targetLocation = targetSpecification.ReferenceLocation; // See if this target is already building under a different build request. If so, we need to wait. int idOfAlreadyBuildingRequest = BuildRequest.InvalidGlobalRequestId; if (_requestEntry.RequestConfiguration.ActivelyBuildingTargets.TryGetValue(targetSpecification.TargetName, out idOfAlreadyBuildingRequest)) { if (idOfAlreadyBuildingRequest != _requestEntry.Request.GlobalRequestId) { // Another request elsewhere is building it. We need to wait. await _requestBuilderCallback.BlockOnTargetInProgress(idOfAlreadyBuildingRequest, targetSpecification.TargetName); // If we come out of here and the target is *still* active, it means the scheduler detected a circular dependency and told us to // continue so we could throw the exception. if (_requestEntry.RequestConfiguration.ActivelyBuildingTargets.ContainsKey(targetSpecification.TargetName)) { ProjectErrorUtilities.ThrowInvalidProject(targetLocation, "CircularDependency", targetSpecification.TargetName); } } else { if (pushType == TargetPushType.AfterTargets) { // If the target we are pushing is supposed to run after the current target and it is already set to run after us then skip adding it now. continue; } // We are already building this target on this request. That's a circular dependency. ProjectErrorUtilities.ThrowInvalidProject(targetLocation, "CircularDependency", targetSpecification.TargetName); } } else { // Does this target exist in our direct parent chain, if it is a before target (since these can cause circular dependency issues) if (pushType == TargetPushType.BeforeTargets || pushType == TargetPushType.Normal) { TargetEntry currentParent = parentTargetEntry; while (currentParent != null) { if (String.Equals(currentParent.Name, targetSpecification.TargetName, StringComparison.OrdinalIgnoreCase)) { // We are already building this target on this request. That's a circular dependency. ProjectErrorUtilities.ThrowInvalidProject(targetLocation, "CircularDependency", targetSpecification.TargetName); } currentParent = currentParent.ParentEntry; } } else { // For an after target, if it is already ANYWHERE on the stack, we don't need to push it because it is already going to run // after whatever target is causing it to be pushed now. bool alreadyPushed = false; foreach (TargetEntry entry in _targetsToBuild) { if (String.Equals(entry.Name, targetSpecification.TargetName, StringComparison.OrdinalIgnoreCase)) { alreadyPushed = true; break; } } if (alreadyPushed) { continue; } } } // Add to the list of targets to push. We don't actually put it on the stack here because we could run into a circular dependency // during this loop, in which case the target stack would be out of whack. TargetEntry newEntry = new TargetEntry(_requestEntry, this as ITargetBuilderCallback, targetSpecification, baseLookup, parentTargetEntry, _componentHost, stopProcessingOnCompletion); newEntry.ErrorTarget = addAsErrorTarget; targetsToPush.Add(newEntry); stopProcessingOnCompletion = false; // The first target on the stack (the last one to be run) always inherits the stopProcessing flag. } // Now push the targets since this operation cannot fail. foreach (TargetEntry targetToPush in targetsToPush) { _targetsToBuild.Push(targetToPush); } bool pushedTargets = (targetsToPush.Count > 0); return pushedTargets; }
/// <summary> /// Pushes the list of targets specified onto the target stack in reverse order specified, so that /// they will be built in the order specified. /// </summary> /// <param name="targets">List of targets to build.</param> /// <param name="parentTargetEntry">The target which should be considered the parent of these targets.</param> /// <param name="baseLookup">The lookup to be used to build these targets.</param> /// <param name="addAsErrorTarget">True if this should be considered an error target.</param> /// <param name="stopProcessingOnCompletion">True if target stack processing should terminate when the last target in the list is processed.</param> /// <param name="pushType">The <see cref="TargetPushType"/> targets being pushed onto the stack.</param> /// <returns>True if we actually pushed any targets, false otherwise.</returns> private async Task <bool> PushTargets(IList <TargetSpecification> targets, TargetEntry parentTargetEntry, Lookup baseLookup, bool addAsErrorTarget, bool stopProcessingOnCompletion, TargetPushType pushType) { List <TargetEntry> targetsToPush = new List <TargetEntry>(targets.Count); // Iterate the list in reverse order so that the first target in the list is the last pushed, and thus the first to be executed. for (int i = targets.Count - 1; i >= 0; i--) { TargetSpecification targetSpecification = targets[i]; if (pushType != TargetPushType.Normal) { // Don't build any Before or After targets for which we already have results. Unlike other targets, // we don't explicitly log a skipped-with-results message because it is not interesting. if (_buildResult.HasResultsForTarget(targetSpecification.TargetName)) { if (_buildResult[targetSpecification.TargetName].ResultCode != TargetResultCode.Skipped) { continue; } } } ElementLocation targetLocation = targetSpecification.ReferenceLocation; // See if this target is already building under a different build request. If so, we need to wait. int idOfAlreadyBuildingRequest = BuildRequest.InvalidGlobalRequestId; if (_requestEntry.RequestConfiguration.ActivelyBuildingTargets.TryGetValue(targetSpecification.TargetName, out idOfAlreadyBuildingRequest)) { if (idOfAlreadyBuildingRequest != _requestEntry.Request.GlobalRequestId) { // Another request elsewhere is building it. We need to wait. await _requestBuilderCallback.BlockOnTargetInProgress(idOfAlreadyBuildingRequest, targetSpecification.TargetName); // If we come out of here and the target is *still* active, it means the scheduler detected a circular dependency and told us to // continue so we could throw the exception. if (_requestEntry.RequestConfiguration.ActivelyBuildingTargets.ContainsKey(targetSpecification.TargetName)) { ProjectErrorUtilities.ThrowInvalidProject(targetLocation, "CircularDependency", targetSpecification.TargetName); } } else { if (pushType == TargetPushType.AfterTargets) { // If the target we are pushing is supposed to run after the current target and it is already set to run after us then skip adding it now. continue; } // We are already building this target on this request. That's a circular dependency. ProjectErrorUtilities.ThrowInvalidProject(targetLocation, "CircularDependency", targetSpecification.TargetName); } } else { // Does this target exist in our direct parent chain, if it is a before target (since these can cause circular dependency issues) if (pushType == TargetPushType.BeforeTargets || pushType == TargetPushType.Normal) { TargetEntry currentParent = parentTargetEntry; while (currentParent != null) { if (String.Equals(currentParent.Name, targetSpecification.TargetName, StringComparison.OrdinalIgnoreCase)) { // We are already building this target on this request. That's a circular dependency. ProjectErrorUtilities.ThrowInvalidProject(targetLocation, "CircularDependency", targetSpecification.TargetName); } currentParent = currentParent.ParentEntry; } } else { // For an after target, if it is already ANYWHERE on the stack, we don't need to push it because it is already going to run // after whatever target is causing it to be pushed now. bool alreadyPushed = false; foreach (TargetEntry entry in _targetsToBuild) { if (String.Equals(entry.Name, targetSpecification.TargetName, StringComparison.OrdinalIgnoreCase)) { alreadyPushed = true; break; } } if (alreadyPushed) { continue; } } } // Add to the list of targets to push. We don't actually put it on the stack here because we could run into a circular dependency // during this loop, in which case the target stack would be out of whack. TargetEntry newEntry = new TargetEntry(_requestEntry, this as ITargetBuilderCallback, targetSpecification, baseLookup, parentTargetEntry, _componentHost, stopProcessingOnCompletion); newEntry.ErrorTarget = addAsErrorTarget; targetsToPush.Add(newEntry); stopProcessingOnCompletion = false; // The first target on the stack (the last one to be run) always inherits the stopProcessing flag. } // Now push the targets since this operation cannot fail. foreach (TargetEntry targetToPush in targetsToPush) { _targetsToBuild.Push(targetToPush); } bool pushedTargets = (targetsToPush.Count > 0); return(pushedTargets); }