Beispiel #1
0
        private bool HasCircularDependenceInTargets(TargetEntry parentTargetEntry, TargetSpecification targetSpecification, out List <string> circularDependenceChain)
        {
            TargetEntry currentParent = parentTargetEntry;

            circularDependenceChain = new List <string>();
            bool hasCircularDependence = false;

            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.
                    hasCircularDependence = true;

                    // Cache the circular dependence chain only when circular dependency occurs.
                    currentParent = parentTargetEntry;
                    circularDependenceChain.Add(targetSpecification.TargetName);
                    while (!String.Equals(currentParent.Name, targetSpecification.TargetName, StringComparison.OrdinalIgnoreCase))
                    {
                        circularDependenceChain.Add(currentParent.Name);
                        currentParent = currentParent.ParentEntry;
                    }

                    circularDependenceChain.Add(currentParent.Name);
                    break;
                }

                currentParent = currentParent.ParentEntry;
            }

            return(hasCircularDependence);
        }
        internal static TargetSpecification FactoryForDeserialization(ITranslator translator)
        {
            var instance = new TargetSpecification();

            ((ITranslatable)instance).Translate(translator);

            return(instance);
        }
Beispiel #3
0
        /// <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="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, 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;
            _expander   = new Expander <ProjectPropertyInstance, ProjectItemInstance>(baseLookup.ReadOnlyLookup, baseLookup.ReadOnlyLookup);
            _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);

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