Example #1
0
        //
        // Main entry point for parser.
        // You pass in the expression you want to parse, and you get an
        // ExpressionTree out the back end.
        //
        internal GenericExpressionNode Parse(string expression, ParserOptions optionSettings, ElementLocation elementLocation)
        {
            // We currently have no support (and no scenarios) for disallowing property references
            // in Conditions.
            ErrorUtilities.VerifyThrow(0 != (optionSettings & ParserOptions.AllowProperties),
                                       "Properties should always be allowed.");

            _options         = optionSettings;
            _elementLocation = elementLocation;

            _lexer = new Scanner(expression, _options);
            if (!_lexer.Advance())
            {
                errorPosition = _lexer.GetErrorPosition();
                ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, _lexer.GetErrorResource(), expression, errorPosition, _lexer.UnexpectedlyFound);
            }
            GenericExpressionNode node = Expr(expression);

            if (!_lexer.IsNext(Token.TokenType.EndOfInput))
            {
                errorPosition = _lexer.GetErrorPosition();
                ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
            }
            return(node);
        }
Example #2
0
        /// <summary>
        /// Construct a target specification.
        /// </summary>
        /// <param name="targetName">The name of the target</param>
        /// <param name="referenceLocation">The location from which it was referred.</param>
        internal TargetSpecification(string targetName, ElementLocation referenceLocation)
        {
            ErrorUtilities.VerifyThrowArgumentLength(targetName, "targetName");
            ErrorUtilities.VerifyThrowArgumentNull(referenceLocation, "referenceLocation");

            this._targetName        = targetName;
            this._referenceLocation = referenceLocation;
        }
Example #3
0
 /// <summary>
 /// Determines how many times the batchable object needs to be executed (each execution is termed a "batch"), and prepares
 /// buckets of items to pass to the object in each batch.
 /// </summary>
 /// <returns>List containing ItemBucket objects, each one representing an execution batch.</returns>
 internal static List <ItemBucket> PrepareBatchingBuckets
 (
     List <string> batchableObjectParameters,
     Lookup lookup,
     ElementLocation elementLocation
 )
 {
     return(PrepareBatchingBuckets(batchableObjectParameters, lookup, null, elementLocation));
 }
Example #4
0
        /// <summary>
        /// Constructor
        /// </summary>
        public TaskFactoryLoggingHost(bool isRunningWithMultipleNodes, ElementLocation elementLocation, BuildLoggingContext loggingContext)
        {
            ErrorUtilities.VerifyThrowArgumentNull(loggingContext, "loggingContext");
            ErrorUtilities.VerifyThrowInternalNull(elementLocation, "elementLocation");

            _activeProxy = true;
            _isRunningWithMultipleNodes = isRunningWithMultipleNodes;
            _loggingContext             = loggingContext;
            _elementLocation            = elementLocation;
        }
Example #5
0
File: TaskHost.cs Project: 3F/IeXod
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="host">The component host</param>
        /// <param name="requestEntry">The build request entry</param>
        /// <param name="taskLocation">The <see cref="ElementLocation"/> of the task.</param>
        /// <param name="targetBuilderCallback">An <see cref="ITargetBuilderCallback"/> to use to invoke targets and build projects.</param>
        public TaskHost(IBuildComponentHost host, BuildRequestEntry requestEntry, ElementLocation taskLocation, ITargetBuilderCallback targetBuilderCallback)
        {
            ErrorUtilities.VerifyThrowArgumentNull(host, "host");
            ErrorUtilities.VerifyThrowArgumentNull(requestEntry, "requestEntry");
            ErrorUtilities.VerifyThrowInternalNull(taskLocation, "taskLocation");

            _host                  = host;
            _requestEntry          = requestEntry;
            _taskLocation          = taskLocation;
            _targetBuilderCallback = targetBuilderCallback;
            _continueOnError       = false;
            _activeProxy           = true;
            _callbackMonitor       = new Object();
        }
Example #6
0
        /// <summary>
        /// Gets the values of the specified metadata for the given item.
        /// The keys in the dictionary returned may be qualified and/or unqualified, exactly
        /// as they are found in the metadata reference.
        /// For example if %(x) is found, the key is "x", if %(z.x) is found, the key is "z.x".
        /// This dictionary in each bucket is used by Expander to expand exactly the same metadata references, so
        /// %(x) is expanded using the key "x", and %(z.x) is expanded using the key "z.x".
        /// </summary>
        /// <returns>the metadata values</returns>
        private static Dictionary <string, string> GetItemMetadataValues
        (
            ProjectItemInstance item,
            Dictionary <string, MetadataReference> consumedMetadataReferences,
            ElementLocation elementLocation
        )
        {
            var itemMetadataValues = new Dictionary <string, string>(consumedMetadataReferences.Count, MSBuildNameIgnoreCaseComparer.Default);

            foreach (KeyValuePair <string, MetadataReference> consumedMetadataReference in consumedMetadataReferences)
            {
                string metadataQualifiedName = consumedMetadataReference.Key;
                string metadataItemName      = consumedMetadataReference.Value.ItemName;
                string metadataName          = consumedMetadataReference.Value.MetadataName;

                if (
                    (metadataItemName != null) &&
                    (0 != String.Compare(item.ItemType, metadataItemName, StringComparison.OrdinalIgnoreCase))
                    )
                {
                    itemMetadataValues[metadataQualifiedName] = String.Empty;
                }
                else
                {
                    try
                    {
                        // This returns String.Empty for both metadata that is undefined and metadata that has
                        // an empty value; they are treated the same.
                        itemMetadataValues[metadataQualifiedName] = ((IItem)item).GetMetadataValueEscaped(metadataName);
                    }
                    catch (InvalidOperationException e)
                    {
                        ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation,
                                                                        "CannotEvaluateItemMetadata", metadataName, e.Message);
                    }
                }
            }

            return(itemMetadataValues);
        }
Example #7
0
        /// <summary>
        /// Indicates to the TaskHost that it is no longer needed.
        /// Called by TaskBuilder when the task using the EngineProxy is done.
        /// </summary>
        internal void MarkAsInactive()
        {
            VerifyActiveProxy();
            _activeProxy = false;

            _loggingContext  = null;
            _elementLocation = null;

            // Clear out the sponsor (who is responsible for keeping the EngineProxy remoting lease alive until the task is done)
            // this will be null if the engineproxy was never sent across an appdomain boundry.
            if (_sponsor != null)
            {
                ILease lease = (ILease)RemotingServices.GetLifetimeService(this);

                if (lease != null)
                {
                    lease.Unregister(_sponsor);
                }

                _sponsor.Close();
                _sponsor = null;
            }
        }
Example #8
0
        /// <summary>
        /// Partitions the items consumed by the batchable object into buckets, where each bucket contains a set of items that
        /// have the same value set on all item metadata consumed by the object.
        /// </summary>
        /// <remarks>
        /// PERF NOTE: Given n items and m batching metadata that produce l buckets, it is usually the case that n > l > m,
        /// because a batchable object typically uses one or two item metadata to control batching, and only has a handful of
        /// buckets. The number of buckets is typically only large if a batchable object is using single-item batching
        /// (where l == n). Any algorithm devised for bucketing therefore, should try to minimize n and l in its complexity
        /// equation. The algorithm below has a complexity of O(n*lg(l)*m/2) in its comparisons, and is effectively O(n) when
        /// l is small, and O(n*lg(n)) in the worst case as l -> n. However, note that the comparison complexity is not the
        /// same as the operational complexity for this algorithm. The operational complexity of this algorithm is actually
        /// O(n*m + n*lg(l)*m/2 + n*l/2 + n + l), which is effectively O(n^2) in the worst case. The additional complexity comes
        /// from the array and metadata operations that are performed. However, those operations are extremely cheap compared
        /// to the comparison operations, which dominate the time spent in this method.
        /// </remarks>
        /// <returns>List containing ItemBucket objects (can be empty), each one representing an execution batch.</returns>
        private static List <ItemBucket> BucketConsumedItems
        (
            Lookup lookup,
            Dictionary <string, ICollection <ProjectItemInstance> > itemListsToBeBatched,
            Dictionary <string, MetadataReference> consumedMetadataReferences,
            ElementLocation elementLocation
        )
        {
            ErrorUtilities.VerifyThrow(itemListsToBeBatched.Count > 0, "Need item types consumed by the batchable object.");
            ErrorUtilities.VerifyThrow(consumedMetadataReferences.Count > 0, "Need item metadata consumed by the batchable object.");

            var buckets = new List <ItemBucket>();

            // Get and iterate through the list of item names that we're supposed to batch on.
            foreach (KeyValuePair <string, ICollection <ProjectItemInstance> > entry in itemListsToBeBatched)
            {
                string itemName = entry.Key;

                // Use the previously-fetched items, if possible
                ICollection <ProjectItemInstance> items = entry.Value ?? lookup.GetItems(itemName);

                if (items != null)
                {
                    foreach (ProjectItemInstance item in items)
                    {
                        // Get this item's values for all the metadata consumed by the batchable object.
                        Dictionary <string, string> itemMetadataValues = GetItemMetadataValues(item, consumedMetadataReferences, elementLocation);

                        // put the metadata into a dummy bucket we can use for searching
                        ItemBucket dummyBucket = ItemBucket.GetDummyBucketForComparisons(itemMetadataValues);

                        // look through all previously created buckets to find a bucket whose items have the same values as
                        // this item for all metadata consumed by the batchable object
                        int matchingBucketIndex = buckets.BinarySearch(dummyBucket);

                        ItemBucket matchingBucket = (matchingBucketIndex >= 0)
                            ? buckets[matchingBucketIndex]
                            : null;

                        // If we didn't find a bucket that matches this item, create a new one, adding
                        // this item to the bucket.
                        if (null == matchingBucket)
                        {
                            matchingBucket = new ItemBucket(itemListsToBeBatched.Keys, itemMetadataValues, lookup, buckets.Count);

                            // make sure to put the new bucket into the appropriate location
                            // in the sorted list as indicated by the binary search
                            // NOTE: observe the ~ operator (bitwise complement) in front of
                            // the index -- see MSDN for more information on the return value
                            // from the List.BinarySearch() method
                            buckets.Insert(~matchingBucketIndex, matchingBucket);
                        }

                        // We already have a bucket for this type of item, so add this item to
                        // the bucket.
                        matchingBucket.AddItem(item);
                    }
                }
            }

            // Put the buckets back in the order in which they were discovered, so that the first
            // item declared in the project file ends up in the first batch passed into the target/task.
            var orderedBuckets = new List <ItemBucket>(buckets.Count);

            for (int i = 0; i < buckets.Count; ++i)
            {
                orderedBuckets.Add(null);
            }

            foreach (ItemBucket bucket in buckets)
            {
                orderedBuckets[bucket.BucketSequenceNumber] = bucket;
            }
            return(orderedBuckets);
        }
Example #9
0
        /// <summary>
        /// Of all the item lists that are referenced in this batchable object, which ones should we
        /// batch on, and which ones should we just pass in wholesale to every invocation of the
        /// target/task?
        ///
        /// Rule #1.  If the user has referenced any *qualified* item metadata such as %(EmbeddedResource.Culture),
        /// then that item list "EmbeddedResource" will definitely get batched.
        ///
        /// Rule #2.  For all the unqualified item metadata such as %(Culture), we make sure that
        /// every single item in every single item list being passed into the task contains a value
        /// for that metadata.  If not, it's an error.  If so, we batch all of those item lists.
        ///
        /// All other item lists will not be batched, and instead will be passed in wholesale to all buckets.
        /// </summary>
        /// <returns>Dictionary containing the item names that should be batched.  If the items match unqualified metadata,
        /// the entire list of items will be returned in the Value.  Otherwise, the Value will be empty, indicating only the
        /// qualified item set (in the Key) should be batched.
        /// </returns>
        private static Dictionary <string, ICollection <ProjectItemInstance> > GetItemListsToBeBatched
        (
            Dictionary <string, MetadataReference> consumedMetadataReferences,   // Key is [string] potentially qualified metadata name
                                                                                 // Value is [struct MetadataReference]
            HashSet <string> consumedItemReferenceNames,
            Lookup lookup,
            ElementLocation elementLocation
        )
        {
            // The keys in this hashtable are the names of the items that we will batch on.
            // The values are always String.Empty (not used).
            var itemListsToBeBatched = new Dictionary <string, ICollection <ProjectItemInstance> >(MSBuildNameIgnoreCaseComparer.Default);

            // Loop through all the metadata references and find the ones that are qualified
            // with an item name.
            foreach (MetadataReference consumedMetadataReference in consumedMetadataReferences.Values)
            {
                if (consumedMetadataReference.ItemName != null)
                {
                    // Rule #1.  Qualified metadata reference.
                    // For metadata references that are qualified with an item name
                    // (e.g., %(EmbeddedResource.Culture) ), we add that item name to the list of
                    // consumed item names, even if the item name wasn't otherwise referenced via
                    // @(...) syntax, and even if every item in the list doesn't necessary contain
                    // a value for this metadata.  This is the special power that you get by qualifying
                    // the metadata reference with an item name.
                    itemListsToBeBatched[consumedMetadataReference.ItemName] = null;

                    // Also add this qualified item to the consumed item references list, because
                    // %(EmbeddedResource.Culture) effectively means that @(EmbeddedResource) is
                    // being consumed, even though we may not see literally "@(EmbeddedResource)"
                    // in the tag anywhere.  Adding it to this list allows us (down below in this
                    // method) to check that every item in this list has a value for each
                    // unqualified metadata reference.
                    consumedItemReferenceNames = consumedItemReferenceNames ?? new HashSet <string>(MSBuildNameIgnoreCaseComparer.Default);
                    consumedItemReferenceNames.Add(consumedMetadataReference.ItemName);
                }
            }

            // Loop through all the metadata references and find the ones that are unqualified.
            foreach (MetadataReference consumedMetadataReference in consumedMetadataReferences.Values)
            {
                if (consumedMetadataReference.ItemName == null)
                {
                    // Rule #2.  Unqualified metadata reference.
                    // For metadata references that are unqualified, every single consumed item
                    // must contain a value for that metadata.  If any item doesn't, it's an error
                    // to use unqualified metadata.
                    if (consumedItemReferenceNames != null)
                    {
                        foreach (string consumedItemName in consumedItemReferenceNames)
                        {
                            // Loop through all the items in the item list.
                            ICollection <ProjectItemInstance> items = lookup.GetItems(consumedItemName);

                            if (items != null)
                            {
                                // Loop through all the items in the BuildItemGroup.
                                foreach (ProjectItemInstance item in items)
                                {
                                    ProjectErrorUtilities.VerifyThrowInvalidProject(
                                        item.HasMetadata(consumedMetadataReference.MetadataName),
                                        elementLocation, "ItemDoesNotContainValueForUnqualifiedMetadata",
                                        item.EvaluatedInclude, consumedItemName, consumedMetadataReference.MetadataName);
                                }
                            }

                            // This item list passes the test of having every single item containing
                            // a value for this metadata.  Therefore, add this item list to the batching list.
                            // Also, to save doing lookup.GetItems again, put the items in the table as the value.
                            itemListsToBeBatched[consumedItemName] = items;
                        }
                    }
                }
            }

            return(itemListsToBeBatched);
        }
Example #10
0
        /// <summary>
        /// Determines how many times the batchable object needs to be executed (each execution is termed a "batch"), and prepares
        /// buckets of items to pass to the object in each batch.
        /// </summary>
        /// <param name="elementLocation"></param>
        /// <param name="batchableObjectParameters"></param>
        /// <param name="lookup"></param>
        /// <param name="implicitBatchableItemType">Any item type that can be considered an implicit input to this batchable object.
        /// This is useful for items inside targets, where the item name is plainly an item type that's an "input" to the object.</param>
        /// <returns>List containing ItemBucket objects, each one representing an execution batch.</returns>
        internal static List <ItemBucket> PrepareBatchingBuckets
        (
            List <string> batchableObjectParameters,
            Lookup lookup,
            string implicitBatchableItemType,
            ElementLocation elementLocation
        )
        {
            if (batchableObjectParameters == null)
            {
                ErrorUtilities.ThrowInternalError("Need the parameters of the batchable object to determine if it can be batched.");
            }

            if (lookup == null)
            {
                ErrorUtilities.ThrowInternalError("Need to specify the lookup.");
            }

            ItemsAndMetadataPair pair = ExpressionShredder.GetReferencedItemNamesAndMetadata(batchableObjectParameters);

            // All the @(itemname) item list references in the tag, including transforms, etc.
            HashSet <string> consumedItemReferences = pair.Items;

            // All the %(itemname.metadataname) references in the tag (not counting those embedded
            // inside item transforms), and note that the itemname portion is optional.
            // The keys in the returned hash table are the qualified metadata names (e.g. "EmbeddedResource.Culture"
            // or just "Culture").  The values are MetadataReference structs, which simply split out the item
            // name (possibly null) and the actual metadata name.
            Dictionary <string, MetadataReference> consumedMetadataReferences = pair.Metadata;

            List <ItemBucket> buckets = null;

            if (consumedMetadataReferences != null && consumedMetadataReferences.Count > 0)
            {
                // Add any item types that we were explicitly told to assume.
                if (implicitBatchableItemType != null)
                {
                    consumedItemReferences = consumedItemReferences ?? new HashSet <string>(MSBuildNameIgnoreCaseComparer.Default);
                    consumedItemReferences.Add(implicitBatchableItemType);
                }

                // This method goes through all the item list references and figures out which ones
                // will be participating in batching, and which ones won't.  We get back a hashtable
                // where the key is the item name that will be participating in batching.  The values
                // are all String.Empty (not used).  This method may return additional item names
                // that weren't represented in "consumedItemReferences"... this would happen if there
                // were qualified metadata references in the consumedMetadataReferences table, such as
                // %(EmbeddedResource.Culture).
                Dictionary <string, ICollection <ProjectItemInstance> > itemListsToBeBatched = GetItemListsToBeBatched(consumedMetadataReferences, consumedItemReferences, lookup, elementLocation);

                // At this point, if there were any metadata references in the tag, but no item
                // references to batch on, we've got a problem because we can't figure out which
                // item lists the user wants us to batch.
                if (itemListsToBeBatched.Count == 0)
                {
                    foreach (string unqualifiedMetadataName in consumedMetadataReferences.Keys)
                    {
                        // Of course, since this throws an exception, there's no way we're ever going
                        // to really loop here... it's just that the foreach is the only way I can
                        // figure out how to get data out of the hashtable without knowing any of the
                        // keys!
                        ProjectErrorUtilities.VerifyThrowInvalidProject(false,
                                                                        elementLocation, "CannotReferenceItemMetadataWithoutItemName", unqualifiedMetadataName);
                    }
                }
                else
                {
                    // If the batchable object consumes item metadata as well as items to be batched,
                    // we need to partition the items consumed by the object.
                    buckets = BucketConsumedItems(lookup, itemListsToBeBatched, consumedMetadataReferences, elementLocation);
                }
            }

            // if the batchable object does not consume any item metadata or items, or if the item lists it consumes are all
            // empty, then the object does not need to be batched
            if ((buckets == null) || (buckets.Count == 0))
            {
                // create a default bucket that references the project items and properties -- this way we always have a bucket
                buckets = new List <ItemBucket>(1);
                buckets.Add(new ItemBucket(null, null, lookup, buckets.Count));
            }

            return(buckets);
        }
Example #11
0
        /// <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);
        }
Example #12
0
        /// <summary>
        /// Runs all of the tasks for this target, batched as necessary.
        /// </summary>
        internal async Task ExecuteTarget(ITaskBuilder taskBuilder, BuildRequestEntry requestEntry, ProjectLoggingContext projectLoggingContext, CancellationToken cancellationToken)
        {
#if MSBUILDENABLEVSPROFILING
            try
            {
                string beginTargetBuild = String.Format(CultureInfo.CurrentCulture, "Build Target {0} in Project {1} - Start", this.Name, projectFullPath);
                DataCollection.CommentMarkProfile(8800, beginTargetBuild);
#endif

            try
            {
                VerifyState(_state, TargetEntryState.Execution);
                ErrorUtilities.VerifyThrow(!_isExecuting, "Target {0} is already executing", _target.Name);
                _cancellationToken = cancellationToken;
                _isExecuting       = true;

                // Generate the batching buckets.  Note that each bucket will get a lookup based on the baseLookup.  This lookup will be in its
                // own scope, which we will collapse back down into the baseLookup at the bottom of the function.
                List <ItemBucket> buckets = BatchingEngine.PrepareBatchingBuckets(GetBatchableParametersForTarget(), _baseLookup, _target.Location);

                WorkUnitResult       aggregateResult      = new WorkUnitResult();
                TargetLoggingContext targetLoggingContext = null;
                bool   targetSuccess   = false;
                int    numberOfBuckets = buckets.Count;
                string projectFullPath = requestEntry.RequestConfiguration.ProjectFullPath;

                string parentTargetName = null;
                if (ParentEntry != null && ParentEntry.Target != null)
                {
                    parentTargetName = ParentEntry.Target.Name;
                }

                for (int i = 0; i < numberOfBuckets; i++)
                {
                    ItemBucket bucket = buckets[i];

                    // If one of the buckets failed, stop building.
                    if (aggregateResult.ActionCode == WorkUnitActionCode.Stop)
                    {
                        break;
                    }

                    targetLoggingContext = projectLoggingContext.LogTargetBatchStarted(projectFullPath, _target, parentTargetName, _buildReason);
                    WorkUnitResult bucketResult = null;
                    targetSuccess = false;

                    Lookup.Scope entryForInference = null;
                    Lookup.Scope entryForExecution = null;

                    try
                    {
                        // This isn't really dependency analysis.  This is up-to-date checking.  Based on this we will be able to determine if we should
                        // run tasks in inference or execution mode (or both) or just skip them altogether.
                        ItemDictionary <ProjectItemInstance> changedTargetInputs;
                        ItemDictionary <ProjectItemInstance> upToDateTargetInputs;
                        Lookup lookupForInference;
                        Lookup lookupForExecution;

                        // UNDONE: (Refactor) Refactor TargetUpToDateChecker to take a logging context, not a logging service.
                        TargetUpToDateChecker    dependencyAnalyzer = new TargetUpToDateChecker(requestEntry.RequestConfiguration.Project, _target, targetLoggingContext.LoggingService, targetLoggingContext.BuildEventContext);
                        DependencyAnalysisResult dependencyResult   = dependencyAnalyzer.PerformDependencyAnalysis(bucket, out changedTargetInputs, out upToDateTargetInputs);

                        switch (dependencyResult)
                        {
                        // UNDONE: Need to enter/leave debugger scope properly for the <Target> element.
                        case DependencyAnalysisResult.FullBuild:
                        case DependencyAnalysisResult.IncrementalBuild:
                        case DependencyAnalysisResult.SkipUpToDate:
                            // Create the lookups used to hold the current set of properties and items
                            lookupForInference = bucket.Lookup;
                            lookupForExecution = bucket.Lookup.Clone();

                            // Push the lookup stack up one so that we are only modifying items and properties in that scope.
                            entryForInference = lookupForInference.EnterScope("ExecuteTarget() Inference");
                            entryForExecution = lookupForExecution.EnterScope("ExecuteTarget() Execution");

                            // if we're doing an incremental build, we need to effectively run the task twice -- once
                            // to infer the outputs for up-to-date input items, and once to actually execute the task;
                            // as a result we need separate sets of item and property collections to track changes
                            if (dependencyResult == DependencyAnalysisResult.IncrementalBuild)
                            {
                                // subset the relevant items to those that are up-to-date
                                foreach (string itemType in upToDateTargetInputs.ItemTypes)
                                {
                                    lookupForInference.PopulateWithItems(itemType, upToDateTargetInputs[itemType]);
                                }

                                // subset the relevant items to those that have changed
                                foreach (string itemType in changedTargetInputs.ItemTypes)
                                {
                                    lookupForExecution.PopulateWithItems(itemType, changedTargetInputs[itemType]);
                                }
                            }

                            // We either have some work to do or at least we need to infer outputs from inputs.
                            bucketResult = await ProcessBucket(taskBuilder, targetLoggingContext, GetTaskExecutionMode(dependencyResult), lookupForInference, lookupForExecution);

                            // Now aggregate the result with the existing known results.  There are four rules, assuming the target was not
                            // skipped due to being up-to-date:
                            // 1. If this bucket failed or was cancelled, the aggregate result is failure.
                            // 2. If this bucket Succeeded and we have not previously failed, the aggregate result is a success.
                            // 3. Otherwise, the bucket was skipped, which has no effect on the aggregate result.
                            // 4. If the bucket's action code says to stop, then we stop, regardless of the success or failure state.
                            if (dependencyResult != DependencyAnalysisResult.SkipUpToDate)
                            {
                                aggregateResult = aggregateResult.AggregateResult(bucketResult);
                            }
                            else
                            {
                                if (aggregateResult.ResultCode == WorkUnitResultCode.Skipped)
                                {
                                    aggregateResult = aggregateResult.AggregateResult(new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null));
                                }
                            }

                            // Pop the lookup scopes, causing them to collapse their values back down into the
                            // bucket's lookup.
                            // NOTE: this order is important because when we infer outputs, we are trying
                            // to produce the same results as would be produced from a full build; as such
                            // if we're doing both the infer and execute steps, we want the outputs from
                            // the execute step to override the outputs of the infer step -- this models
                            // the full build scenario more correctly than if the steps were reversed
                            entryForInference.LeaveScope();
                            entryForInference = null;
                            entryForExecution.LeaveScope();
                            entryForExecution = null;
                            targetSuccess     = (bucketResult != null) && (bucketResult.ResultCode == WorkUnitResultCode.Success);
                            break;

                        case DependencyAnalysisResult.SkipNoInputs:
                        case DependencyAnalysisResult.SkipNoOutputs:
                            // We have either no inputs or no outputs, so there is nothing to do.
                            targetSuccess = true;
                            break;
                        }
                    }
                    catch (InvalidProjectFileException e)
                    {
                        // Make sure the Invalid Project error gets logged *before* TargetFinished.  Otherwise,
                        // the log is confusing.
                        targetLoggingContext.LogInvalidProjectFileError(e);

                        if (null != entryForInference)
                        {
                            entryForInference.LeaveScope();
                        }

                        if (null != entryForExecution)
                        {
                            entryForExecution.LeaveScope();
                        }

                        aggregateResult = aggregateResult.AggregateResult(new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null));
                    }
                    finally
                    {
                        // Don't log the last target finished event until we can process the target outputs as we want to attach them to the
                        // last target batch.
                        if (targetLoggingContext != null && i < numberOfBuckets - 1)
                        {
                            targetLoggingContext.LogTargetBatchFinished(projectFullPath, targetSuccess, null);
                            targetLoggingContext = null;
                        }
                    }
                }

                // Produce the final results.
                List <TaskItem> targetOutputItems = new List <TaskItem>();

                try
                {
                    // If any legacy CallTarget operations took place, integrate them back in to the main lookup now.
                    LeaveLegacyCallTargetScopes();

                    // Publish the items for each bucket back into the baseLookup.  Note that EnterScope() was actually called on each
                    // bucket inside of the ItemBucket constructor, which is why you don't see it anywhere around here.
                    foreach (ItemBucket bucket in buckets)
                    {
                        bucket.LeaveScope();
                    }

                    string          targetReturns         = _target.Returns;
                    ElementLocation targetReturnsLocation = _target.ReturnsLocation;

                    // If there are no targets in the project file that use the "Returns" attribute, that means that we
                    // revert to the legacy behavior in the case where Returns is not specified (null, rather
                    // than the empty string, which indicates no returns).  Legacy behavior is for all
                    // of the target's Outputs to be returned.
                    // On the other hand, if there is at least one target in the file that uses the Returns attribute,
                    // then all targets in the file are run according to the new behaviour (return nothing unless otherwise
                    // specified by the Returns attribute).
                    if (targetReturns == null)
                    {
                        if (!_target.ParentProjectSupportsReturnsAttribute)
                        {
                            targetReturns         = _target.Outputs;
                            targetReturnsLocation = _target.OutputsLocation;
                        }
                    }

                    if (!String.IsNullOrEmpty(targetReturns))
                    {
                        // Determine if we should keep duplicates.
                        bool keepDupes = ConditionEvaluator.EvaluateCondition
                                         (
                            _target.KeepDuplicateOutputs,
                            ParserOptions.AllowPropertiesAndItemLists,
                            _expander,
                            ExpanderOptions.ExpandPropertiesAndItems,
                            requestEntry.ProjectRootDirectory,
                            _target.KeepDuplicateOutputsLocation,
                            projectLoggingContext.LoggingService,
                            projectLoggingContext.BuildEventContext, FileSystems.Default);

                        // NOTE: we need to gather the outputs in batches, because the output specification may reference item metadata
                        // Also, we are using the baseLookup, which has possibly had changes made to it since the project started.  Because of this, the
                        // set of outputs calculated here may differ from those which would have been calculated at the beginning of the target.  It is
                        // assumed the user intended this.
                        List <ItemBucket> batchingBuckets = BatchingEngine.PrepareBatchingBuckets(GetBatchableParametersForTarget(), _baseLookup, _target.Location);

                        if (keepDupes)
                        {
                            foreach (ItemBucket bucket in batchingBuckets)
                            {
                                targetOutputItems.AddRange(bucket.Expander.ExpandIntoTaskItemsLeaveEscaped(targetReturns, ExpanderOptions.ExpandAll, targetReturnsLocation));
                            }
                        }
                        else
                        {
                            HashSet <TaskItem> addedItems = new HashSet <TaskItem>();
                            foreach (ItemBucket bucket in batchingBuckets)
                            {
                                IList <TaskItem> itemsToAdd = bucket.Expander.ExpandIntoTaskItemsLeaveEscaped(targetReturns, ExpanderOptions.ExpandAll, targetReturnsLocation);

                                foreach (TaskItem item in itemsToAdd)
                                {
                                    if (!addedItems.Contains(item))
                                    {
                                        targetOutputItems.Add(item);
                                        addedItems.Add(item);
                                    }
                                }
                            }
                        }
                    }
                }
                finally
                {
                    if (targetLoggingContext != null)
                    {
                        // log the last target finished since we now have the target outputs.
                        targetLoggingContext.LogTargetBatchFinished(projectFullPath, targetSuccess, targetOutputItems != null && targetOutputItems.Count > 0 ? targetOutputItems : null);
                    }
                }

                _targetResult = new TargetResult(targetOutputItems.ToArray(), aggregateResult);

                if (aggregateResult.ResultCode == WorkUnitResultCode.Failed && aggregateResult.ActionCode == WorkUnitActionCode.Stop)
                {
                    _state = TargetEntryState.ErrorExecution;
                }
                else
                {
                    _state = TargetEntryState.Completed;
                }
            }
            finally
            {
                _isExecuting = false;
            }
#if MSBUILDENABLEVSPROFILING
        }

        finally
        {
            string endTargetBuild = String.Format(CultureInfo.CurrentCulture, "Build Target {0} in Project {1} - End", this.Name, projectFullPath);
            DataCollection.CommentMarkProfile(8801, endTargetBuild);
        }
#endif
        }
Example #13
0
        /// <summary>
        /// Is the given task name able to be created by the task factory. In the case of an assembly task factory
        /// this question is answered by checking the assembly wrapped by the task factory to see if it exists.
        /// </summary>
        internal bool TaskNameCreatableByFactory(string taskName, IDictionary <string, string> taskIdentityParameters, string taskProjectFile, TargetLoggingContext targetLoggingContext, ElementLocation elementLocation)
        {
            if (!TaskIdentityParametersMatchFactory(_factoryIdentityParameters, taskIdentityParameters))
            {
                return(false);
            }

            // Parameters match, so now we check to see if the task exists.
            LoadedType taskClass = null;

            try
            {
                ErrorUtilities.VerifyThrowArgumentLength(taskName, "TaskName");
                taskClass = _typeLoader.ReflectionOnlyLoad(taskName, _loadedType.Assembly);
                if (taskClass != null)
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            catch (TargetInvocationException e)
            {
                // Exception thrown by the called code itself
                // Log the stack, so the task vendor can fix their code
                ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, Environment.NewLine + e.InnerException.ToString());
            }
            catch (ReflectionTypeLoadException e)
            {
                // ReflectionTypeLoadException.LoaderExceptions may contain nulls
                foreach (Exception exception in e.LoaderExceptions)
                {
                    if (exception != null)
                    {
                        targetLoggingContext.LogError(new BuildEventFileInfo(taskProjectFile), "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, exception.Message);
                    }
                }

                ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, e.Message);
            }
            catch (ArgumentNullException e)
            {
                // taskName may be null
                ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, e.Message);
            }
            catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception.
            {
                if (ExceptionHandling.NotExpectedReflectionException(e))
                {
                    throw;
                }

                ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, e.Message);
            }

            return(false);
        }
Example #14
0
        /// <summary>
        /// Create an instance of the wrapped ITask for a batch run of the task.
        /// </summary>
        internal ITask CreateTaskInstance(ElementLocation taskLocation, TaskLoggingContext taskLoggingContext, IBuildComponentHost buildComponentHost, IDictionary <string, string> taskIdentityParameters,
#if FEATURE_APPDOMAIN
                                          AppDomainSetup appDomainSetup,
#endif
                                          bool isOutOfProc)
        {
            bool useTaskFactory = false;
            IDictionary <string, string> mergedParameters = null;

            _taskLoggingContext = taskLoggingContext;

            // Optimization for the common (vanilla AssemblyTaskFactory) case -- only calculate
            // the task factory parameters if we have any to calculate; otherwise even if we
            // still launch the task factory, it will be with parameters corresponding to the
            // current process.
            if ((_factoryIdentityParameters != null && _factoryIdentityParameters.Count > 0) || (taskIdentityParameters != null && taskIdentityParameters.Count > 0))
            {
                VerifyThrowIdentityParametersValid(taskIdentityParameters, taskLocation, _taskName, "MSBuildRuntime", "MSBuildArchitecture");

                mergedParameters = MergeTaskFactoryParameterSets(_factoryIdentityParameters, taskIdentityParameters);
                useTaskFactory   = !NativeMethodsShared.IsMono &&
                                   (_taskHostFactoryExplicitlyRequested ||
                                    !TaskHostParametersMatchCurrentProcess(mergedParameters));
            }
            else
            {
                // if we don't have any task host parameters specified on either the using task or the
                // task invocation, then we will run in-proc UNLESS "TaskHostFactory" is explicitly specified
                // as the task factory.
                useTaskFactory = _taskHostFactoryExplicitlyRequested;
            }

            if (useTaskFactory)
            {
                ErrorUtilities.VerifyThrowInternalNull(buildComponentHost, "buildComponentHost");

                mergedParameters = mergedParameters ?? new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

                string runtime      = null;
                string architecture = null;

                if (!mergedParameters.TryGetValue(XMakeAttributes.runtime, out runtime))
                {
                    mergedParameters[XMakeAttributes.runtime] = XMakeAttributes.MSBuildRuntimeValues.clr4;
                }

                if (!mergedParameters.TryGetValue(XMakeAttributes.architecture, out architecture))
                {
                    mergedParameters[XMakeAttributes.architecture] = XMakeAttributes.GetCurrentMSBuildArchitecture();
                }

                TaskHostTask task = new TaskHostTask(taskLocation, taskLoggingContext, buildComponentHost, mergedParameters, _loadedType
#if FEATURE_APPDOMAIN
                                                     , appDomainSetup
#endif
                                                     );
                return(task);
            }
            else
            {
#if FEATURE_APPDOMAIN
                AppDomain taskAppDomain = null;
#endif

                ITask taskInstance = TaskLoader.CreateTask(_loadedType, _taskName, taskLocation.File, taskLocation.Line, taskLocation.Column, new TaskLoader.LogError(ErrorLoggingDelegate)
#if FEATURE_APPDOMAIN
                                                           , appDomainSetup
#endif
                                                           , isOutOfProc
#if FEATURE_APPDOMAIN
                                                           , out taskAppDomain
#endif
                                                           );

#if FEATURE_APPDOMAIN
                if (taskAppDomain != null)
                {
                    _tasksAndAppDomains[taskInstance] = taskAppDomain;
                }
#endif

                return(taskInstance);
            }
        }
Example #15
0
        /// <summary>
        /// Initialize the factory from the task registry
        /// </summary>
        internal LoadedType InitializeFactory
        (
            AssemblyLoadInfo loadInfo,
            string taskName,
            IDictionary <string, TaskPropertyInfo> taskParameters,
            string taskElementContents,
            IDictionary <string, string> taskFactoryIdentityParameters,
            bool taskHostFactoryExplicitlyRequested,
            TargetLoggingContext targetLoggingContext,
            ElementLocation elementLocation,
            string taskProjectFile
        )
        {
            ErrorUtilities.VerifyThrowArgumentNull(loadInfo, "loadInfo");
            VerifyThrowIdentityParametersValid(taskFactoryIdentityParameters, elementLocation, taskName, "Runtime", "Architecture");

            if (taskFactoryIdentityParameters != null)
            {
                _factoryIdentityParameters = new Dictionary <string, string>(taskFactoryIdentityParameters, StringComparer.OrdinalIgnoreCase);
            }

            _taskHostFactoryExplicitlyRequested = taskHostFactoryExplicitlyRequested;

            try
            {
                ErrorUtilities.VerifyThrowArgumentLength(taskName, "taskName");
                _taskName   = taskName;
                _loadedType = _typeLoader.Load(taskName, loadInfo);
                ProjectErrorUtilities.VerifyThrowInvalidProject(_loadedType != null, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty);
            }
            catch (TargetInvocationException e)
            {
                // Exception thrown by the called code itself
                // Log the stack, so the task vendor can fix their code
                ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, Environment.NewLine + e.InnerException.ToString());
            }
            catch (ReflectionTypeLoadException e)
            {
                // ReflectionTypeLoadException.LoaderExceptions may contain nulls
                foreach (Exception exception in e.LoaderExceptions)
                {
                    if (exception != null)
                    {
                        targetLoggingContext.LogError(new BuildEventFileInfo(taskProjectFile), "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, exception.Message);
                    }
                }

                ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message);
            }
            catch (ArgumentNullException e)
            {
                // taskName may be null
                ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message);
            }
            catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception.
            {
                if (ExceptionHandling.NotExpectedReflectionException(e))
                {
                    throw;
                }

                ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message);
            }

            return(_loadedType);
        }
Example #16
0
        /// <summary>
        /// Returns a list of all items in the provided item group whose itemspecs match the specification, after it is split and any wildcards are expanded.
        /// If no items match, returns null.
        /// </summary>
        /// <param name="items">The items to match</param>
        /// <param name="specification">The specification to match against the items.</param>
        /// <param name="specificationLocation">The specification to match against the provided items</param>
        /// <param name="expander">The expander to use</param>
        /// <returns>A list of matching items</returns>
        private List <ProjectItemInstance> FindItemsMatchingSpecification
        (
            ICollection <ProjectItemInstance> items,
            string specification,
            ElementLocation specificationLocation,
            Expander <ProjectPropertyInstance, ProjectItemInstance> expander
        )
        {
            if (items.Count == 0 || specification.Length == 0)
            {
                return(null);
            }

            // This is a hashtable whose key is the filename for the individual items
            // in the Exclude list, after wildcard expansion.
            HashSet <string> specificationsToFind = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            // Split by semicolons
            var specificationPieces = expander.ExpandIntoStringListLeaveEscaped(specification, ExpanderOptions.ExpandAll, specificationLocation);

            foreach (string piece in specificationPieces)
            {
                // Take each individual path or file expression, and expand any
                // wildcards.  Then loop through each file returned, and add it
                // to our hashtable.

                // Don't unescape wildcards just yet - if there were any escaped, the caller wants to treat them
                // as literals. Everything else is safe to unescape at this point, since we're only matching
                // against the file system.
                string[] fileList = _engineFileUtilities.GetFileListEscaped(Project.Directory, piece);

                foreach (string file in fileList)
                {
                    // Now unescape everything, because this is the end of the road for this filename.
                    // We're just going to compare it to the unescaped include path to filter out the
                    // file excludes.
                    specificationsToFind.Add(EscapingUtilities.UnescapeAll(file));
                }
            }

            if (specificationsToFind.Count == 0)
            {
                return(null);
            }

            // Now loop through our list and filter out any that match a
            // filename in the remove list.
            List <ProjectItemInstance> itemsRemoved = new List <ProjectItemInstance>();

            foreach (ProjectItemInstance item in items)
            {
                // Even if the case for the excluded files is different, they
                // will still get excluded, as expected.  However, if the excluded path
                // references the same file in a different way, such as by relative
                // path instead of absolute path, we will not realize that they refer
                // to the same file, and thus we will not exclude it.
                if (specificationsToFind.Contains(item.EvaluatedInclude))
                {
                    itemsRemoved.Add(item);
                }
            }

            return(itemsRemoved);
        }
Example #17
0
 /// <summary>
 /// Empty impl
 /// </summary>
 Task <ITargetResult[]> ITargetBuilderCallback.LegacyCallTarget(string[] targets, bool continueOnError, ElementLocation referenceLocation)
 {
     throw new NotImplementedException();
 }
Example #18
0
        /// <summary>
        /// Invokes the specified targets using Dev9 behavior.
        /// </summary>
        /// <param name="targets">The targets to build.</param>
        /// <param name="continueOnError">True to continue building the remaining targets if one fails.</param>
        /// <param name="taskLocation">The <see cref="ElementLocation"/> of the task.</param>
        /// <returns>The results for each target.</returns>
        /// <remarks>
        /// Dev9 behavior refers to the following:
        /// 1. The changes made during the calling target up to this point are NOT visible to this target.
        /// 2. The changes made by this target are NOT visible to the calling target.
        /// 3. Changes made by the calling target OVERRIDE changes made by this target.
        /// </remarks>
        async Task <ITargetResult[]> ITargetBuilderCallback.LegacyCallTarget(string[] targets, bool continueOnError, ElementLocation taskLocation)
        {
            List <TargetSpecification> targetToPush = new List <TargetSpecification>();

            ITargetResult[] results = new TargetResult[targets.Length];
            bool            originalLegacyCallTargetContinueOnError = _legacyCallTargetContinueOnError;

            _legacyCallTargetContinueOnError = _legacyCallTargetContinueOnError || continueOnError;

            // Our lookup is the one used at the beginning of the calling target.
            Lookup callTargetLookup = _baseLookup;

            // We now record this lookup in the calling target's entry so that it may
            // leave the scope just before it commits its own changes to the base lookup.
            TargetEntry currentTargetEntry = _targetsToBuild.Peek();

            currentTargetEntry.EnterLegacyCallTargetScope(callTargetLookup);

            ITaskBuilder taskBuilder = _componentHost.GetComponent(BuildComponentType.TaskBuilder) as ITaskBuilder;

            try
            {
                // Flag set to true if one of the targets we call fails.
                bool errorResult = false;

                // Now walk through the list of targets, invoking each one.
                for (int i = 0; i < targets.Length; i++)
                {
                    if (_cancellationToken.IsCancellationRequested || errorResult)
                    {
                        results[i] = new TargetResult(Array.Empty <TaskItem>(), new WorkUnitResult(WorkUnitResultCode.Skipped, WorkUnitActionCode.Continue, null));
                    }
                    else
                    {
                        targetToPush.Clear();
                        targetToPush.Add(new TargetSpecification(targets[i], taskLocation));

                        // We push the targets one at a time to emulate the original CallTarget behavior.
                        bool pushed = await PushTargets(targetToPush, currentTargetEntry, callTargetLookup, false, true, TargetBuiltReason.None);

                        ErrorUtilities.VerifyThrow(pushed, "Failed to push any targets onto the stack.  Target: {0} Current Target: {1}", targets[i], currentTargetEntry.Target.Name);
                        await ProcessTargetStack(taskBuilder);

                        if (!_cancellationToken.IsCancellationRequested)
                        {
                            results[i] = _buildResult[targets[i]];
                            if (results[i].ResultCode == TargetResultCode.Failure)
                            {
                                errorResult = true;
                            }
                        }
                        else
                        {
                            results[i] = new TargetResult(Array.Empty <TaskItem>(), new WorkUnitResult(WorkUnitResultCode.Skipped, WorkUnitActionCode.Continue, null));
                        }
                    }
                }
            }
            finally
            {
                // Restore the state of the TargetBuilder to that it was prior to the CallTarget call.
                // Any targets we have pushed on at this point we need to get rid of since we aren't going to process them.
                // If there were normal task errors, standard error handling semantics would have taken care of them.
                // If there was an exception, such as a circular dependency error, items may still be on the stack so we must clear them.
                while (!Object.ReferenceEquals(_targetsToBuild.Peek(), currentTargetEntry))
                {
                    _targetsToBuild.Pop();
                }

                _legacyCallTargetContinueOnError = originalLegacyCallTargetContinueOnError;
                ((IBuildComponent)taskBuilder).ShutdownComponent();
            }

            return(results);
        }