internal static TargetSpecification FactoryForDeserialization(ITranslator translator) { var instance = new TargetSpecification(); ((ITranslatable)instance).Translate(translator); return(instance); }
/// <summary> /// The constructor. /// </summary> /// <param name="requestEntry">The build request entry for the target.</param> /// <param name="targetBuilderCallback">The target builder callback.</param> /// <param name="targetSpecification">The specification for the target to build.</param> /// <param name="baseLookup">The lookup to use.</param> /// <param name="parentTarget">The parent of this entry, if any.</param> /// <param name="buildReason">The reason the parent built this target.</param> /// <param name="host">The Build Component Host to use.</param> /// <param name="stopProcessingOnCompletion">True if the target builder should stop processing the current target stack when this target is complete.</param> internal TargetEntry(BuildRequestEntry requestEntry, ITargetBuilderCallback targetBuilderCallback, TargetSpecification targetSpecification, Lookup baseLookup, TargetEntry parentTarget, TargetBuiltReason buildReason, IBuildComponentHost host, bool stopProcessingOnCompletion) { ErrorUtilities.VerifyThrowArgumentNull(requestEntry, "requestEntry"); ErrorUtilities.VerifyThrowArgumentNull(targetBuilderCallback, "targetBuilderCallback"); ErrorUtilities.VerifyThrowArgumentNull(targetSpecification, "targetName"); ErrorUtilities.VerifyThrowArgumentNull(baseLookup, "lookup"); ErrorUtilities.VerifyThrowArgumentNull(host, "host"); _requestEntry = requestEntry; _targetBuilderCallback = targetBuilderCallback; _targetSpecification = targetSpecification; _parentTarget = parentTarget; _buildReason = buildReason; _expander = new Expander <ProjectPropertyInstance, ProjectItemInstance>(baseLookup, baseLookup, FileSystems.Default); _state = TargetEntryState.Dependencies; _baseLookup = baseLookup; _host = host; this.StopProcessingOnCompletion = stopProcessingOnCompletion; }
/// <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="buildReason">The reason the target is being built by the parent.</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, TargetBuiltReason buildReason) { 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 (buildReason == TargetBuiltReason.BeforeTargets || buildReason == TargetBuiltReason.AfterTargets) { // 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, null); // 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 (buildReason == TargetBuiltReason.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 (buildReason == TargetBuiltReason.BeforeTargets || buildReason == TargetBuiltReason.DependsOn || buildReason == TargetBuiltReason.None) { 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, buildReason, _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); }