Example #1
0
 protected override void SaveItems(ImmutableList <I> items, OrderedItemDataCollection.Builder listBuilder)
 {
     foreach (var item in items)
     {
         listBuilder.Add(new ItemData(item, _itemElement, _elementOrder, _conditionResult));
     }
 }
            protected override void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                if (!_conditionResult)
                {
                    return;
                }

                SetMatchItemSpec();
                _itemsToUpdate ??= ImmutableList.CreateBuilder <ItemBatchingContext>();
                _itemsToUpdate.Clear();

                for (int i = 0; i < listBuilder.Count; i++)
                {
                    var itemData = listBuilder[i];

                    var matchResult = _matchItemSpec(_itemSpec, itemData.Item);

                    if (matchResult.IsMatch)
                    {
                        listBuilder[i] = UpdateItem(listBuilder[i], matchResult.CapturedItemsFromReferencedItemTypes);
                    }
                }

                DecorateItemsWithMetadata(_itemsToUpdate.ToImmutableList(), _metadata, _needToExpandMetadataForEachItem);
            }
Example #3
0
            protected virtual void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                var items = SelectItems(listBuilder, globsToIgnore);

                MutateItems(items);
                SaveItems(items, listBuilder);
            }
Example #4
0
 public void Apply(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
 {
     MSBuildEventSource.Log.ApplyLazyItemOperationsStart(_itemElement.ItemType);
     using (_lazyEvaluator._evaluationProfiler.TrackElement(_itemElement))
     {
         ApplyImpl(listBuilder, globsToIgnore);
     }
     MSBuildEventSource.Log.ApplyLazyItemOperationsStop(_itemElement.ItemType);
 }
            /// <summary>
            /// Apply the Remove operation.
            /// </summary>
            /// <remarks>
            /// This override exists to apply the removing-everything short-circuit and to avoid creating a redundant list of items to remove.
            /// </remarks>
            protected override void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                if (!_conditionResult)
                {
                    return;
                }

                bool matchingOnMetadata = !_matchOnMetadata.IsEmpty;

                if (!matchingOnMetadata)
                {
                    if (ItemspecContainsASingleBareItemReference(_itemSpec, _itemElement.ItemType))
                    {
                        // Perf optimization: If the Remove operation references itself (e.g. <I Remove="@(I)"/>)
                        // then all items are removed and matching is not necessary
                        listBuilder.Clear();
                        return;
                    }

                    if (listBuilder.Count >= Traits.Instance.DictionaryBasedItemRemoveThreshold)
                    {
                        // Perf optimization: If the number of items in the running list is large, construct a dictionary,
                        // enumerate all items referenced by the item spec, and perform dictionary look-ups to find items
                        // to remove.
                        IList <string> matches = _itemSpec.IntersectsWith(listBuilder.Dictionary);
                        listBuilder.RemoveAll(matches);
                        return;
                    }
                }

                // todo Perf: do not match against the globs: https://github.com/dotnet/msbuild/issues/2329
                HashSet <I> items = null;

                foreach (ItemData item in listBuilder)
                {
                    bool isMatch = matchingOnMetadata ? MatchesItemOnMetadata(item.Item) : _itemSpec.MatchesItem(item.Item);
                    if (isMatch)
                    {
                        items ??= new HashSet <I>();
                        items.Add(item.Item);
                    }
                }
                if (items is not null)
                {
                    listBuilder.RemoveAll(items);
                }
            }
Example #6
0
            public void Apply(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
#if DEBUG
                CheckInvariant();
#endif

                Operation.Apply(listBuilder, globsToIgnore);

                // cache results if somebody is referencing this operation
                if (_isReferenced)
                {
                    AddItemsToCache(globsToIgnore, listBuilder.ToImmutable());
                }
#if DEBUG
                _applyCalls++;
                CheckInvariant();
#endif
            }
Example #7
0
            protected override ImmutableList <I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                var itemsToAdd = ImmutableList.CreateBuilder <I>();

                Lazy <Func <string, bool> > excludeTester = null;

                ImmutableList <string> .Builder excludePatterns = ImmutableList.CreateBuilder <string>();
                if (_excludes != null)
                {
                    // STEP 4: Evaluate, split, expand and subtract any Exclude
                    foreach (string exclude in _excludes)
                    {
                        string excludeExpanded = _expander.ExpandIntoStringLeaveEscaped(exclude, ExpanderOptions.ExpandPropertiesAndItems, _itemElement.ExcludeLocation);
                        var    excludeSplits   = ExpressionShredder.SplitSemiColonSeparatedList(excludeExpanded);
                        excludePatterns.AddRange(excludeSplits);
                    }

                    if (excludePatterns.Count > 0)
                    {
                        excludeTester = new Lazy <Func <string, bool> >(() => EngineFileUtilities.GetFileSpecMatchTester(excludePatterns, _rootDirectory));
                    }
                }

                ISet <string> excludePatternsForGlobs = null;

                foreach (var fragment in _itemSpec.Fragments)
                {
                    if (fragment is ItemSpec <P, I> .ItemExpressionFragment itemReferenceFragment)
                    {
                        // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string
                        var itemsFromExpression = _expander.ExpandExpressionCaptureIntoItems(
                            itemReferenceFragment.Capture,
                            _evaluatorData,
                            _itemFactory,
                            ExpanderOptions.ExpandItems,
                            includeNullEntries: false,
                            isTransformExpression: out _,
                            elementLocation: _itemElement.IncludeLocation);

                        itemsToAdd.AddRange(
                            excludeTester != null
                                ? itemsFromExpression.Where(item => !excludeTester.Value(item.EvaluatedInclude))
                                : itemsFromExpression);
                    }
                    else if (fragment is ValueFragment valueFragment)
                    {
                        string value = valueFragment.TextFragment;

                        if (excludeTester?.Value(EscapingUtilities.UnescapeAll(value)) != true)
                        {
                            var item = _itemFactory.CreateItem(value, value, _itemElement.ContainingProject.FullPath);
                            itemsToAdd.Add(item);
                        }
                    }
                    else if (fragment is GlobFragment globFragment)
                    {
                        // If this item is behind a false condition and represents a full drive/filesystem scan, expanding it is
                        // almost certainly undesired. It should be skipped to avoid evaluation taking an excessive amount of time.
                        bool skipGlob = !_conditionResult && globFragment.IsFullFileSystemScan && !Traits.Instance.EscapeHatches.AlwaysEvaluateDangerousGlobs;
                        if (!skipGlob)
                        {
                            string glob = globFragment.TextFragment;

                            if (excludePatternsForGlobs == null)
                            {
                                excludePatternsForGlobs = BuildExcludePatternsForGlobs(globsToIgnore, excludePatterns);
                            }

                            string[] includeSplitFilesEscaped;
                            if (MSBuildEventSource.Log.IsEnabled())
                            {
                                MSBuildEventSource.Log.ExpandGlobStart(_rootDirectory, glob, string.Join(", ", excludePatternsForGlobs));
                            }
                            using (_lazyEvaluator._evaluationProfiler.TrackGlob(_rootDirectory, glob, excludePatternsForGlobs))
                            {
                                includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(
                                    _rootDirectory,
                                    glob,
                                    excludePatternsForGlobs
                                    );
                            }
                            if (MSBuildEventSource.Log.IsEnabled())
                            {
                                MSBuildEventSource.Log.ExpandGlobStop(_rootDirectory, glob, string.Join(", ", excludePatternsForGlobs));
                            }

                            foreach (string includeSplitFileEscaped in includeSplitFilesEscaped)
                            {
                                itemsToAdd.Add(_itemFactory.CreateItem(includeSplitFileEscaped, glob, _itemElement.ContainingProject.FullPath));
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(fragment.GetType().ToString());
                    }
                }

                return(itemsToAdd.ToImmutable());
            }
Example #8
0
 protected virtual void SaveItems(ImmutableList <I> items, OrderedItemDataCollection.Builder listBuilder)
 {
 }
Example #9
0
 protected virtual ImmutableList <I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
 {
     return(listBuilder.Select(itemData => itemData.Item)
            .ToImmutableList());
 }
Example #10
0
            private static void ProcessNonWildCardItemUpdates(Dictionary <string, UpdateOperation> itemsWithNoWildcards, OrderedItemDataCollection.Builder items)
            {
#if DEBUG
                ErrorUtilities.VerifyThrow(itemsWithNoWildcards.All(fragment => !MSBuildConstants.CharactersForExpansion.Any(fragment.Key.Contains)), $"{nameof(itemsWithNoWildcards)} should not contain any text fragments with wildcards.");
#endif
                if (itemsWithNoWildcards.Count > 0)
                {
                    for (int i = 0; i < items.Count; i++)
                    {
                        string fullPath = FileUtilities.GetFullPath(items[i].Item.EvaluatedIncludeEscaped, items[i].Item.ProjectDirectory);
                        if (itemsWithNoWildcards.TryGetValue(fullPath, out UpdateOperation op))
                        {
                            items[i] = op.UpdateItem(items[i]);
                        }
                    }
                    itemsWithNoWildcards.Clear();
                }
            }
Example #11
0
            private static OrderedItemDataCollection.Builder ComputeItems(LazyItemList lazyItemList, ImmutableHashSet <string> globsToIgnore)
            {
                // Stack of operations up to the first one that's cached (exclusive)
                Stack <LazyItemList> itemListStack = new Stack <LazyItemList>();

                OrderedItemDataCollection.Builder items = null;

                // Keep a separate stack of lists of globs to ignore that only gets modified for Remove operations
                Stack <ImmutableHashSet <string> > globsToIgnoreStack = null;

                for (var currentList = lazyItemList; currentList != null; currentList = currentList._previous)
                {
                    var globsToIgnoreFromFutureOperations = globsToIgnoreStack?.Peek() ?? globsToIgnore;

                    OrderedItemDataCollection itemsFromCache;
                    if (currentList._memoizedOperation.TryGetFromCache(globsToIgnoreFromFutureOperations, out itemsFromCache))
                    {
                        // the base items on top of which to apply the uncached operations are the items of the first operation that is cached
                        items = itemsFromCache.ToBuilder();
                        break;
                    }

                    // If this is a remove operation, then add any globs that will be removed
                    //  to a list of globs to ignore in previous operations
                    if (currentList._memoizedOperation.Operation is RemoveOperation removeOperation)
                    {
                        globsToIgnoreStack ??= new Stack <ImmutableHashSet <string> >();

                        var globsToIgnoreForPreviousOperations = removeOperation.GetRemovedGlobs();
                        foreach (var globToRemove in globsToIgnoreFromFutureOperations)
                        {
                            globsToIgnoreForPreviousOperations.Add(globToRemove);
                        }

                        globsToIgnoreStack.Push(globsToIgnoreForPreviousOperations.ToImmutable());
                    }

                    itemListStack.Push(currentList);
                }

                if (items == null)
                {
                    items = OrderedItemDataCollection.CreateBuilder();
                }

                ImmutableHashSet <string> currentGlobsToIgnore = globsToIgnoreStack == null ? globsToIgnore : globsToIgnoreStack.Peek();

                Dictionary <string, UpdateOperation> itemsWithNoWildcards = new Dictionary <string, UpdateOperation>(StringComparer.OrdinalIgnoreCase);
                bool addedToBatch = false;

                // Walk back down the stack of item lists applying operations
                while (itemListStack.Count > 0)
                {
                    var currentList = itemListStack.Pop();

                    if (currentList._memoizedOperation.Operation is UpdateOperation op)
                    {
                        bool addToBatch = true;
                        int  i;
                        // The TextFragments are things like abc.def or x*y.*z.
                        for (i = 0; i < op.Spec.Fragments.Count; i++)
                        {
                            ItemSpecFragment frag = op.Spec.Fragments[i];
                            if (MSBuildConstants.CharactersForExpansion.Any(frag.TextFragment.Contains))
                            {
                                // Fragment contains wild cards, items, or properties. Cannot batch over it using a dictionary.
                                addToBatch = false;
                                break;
                            }

                            string fullPath = FileUtilities.GetFullPath(frag.TextFragment, frag.ProjectDirectory);
                            if (itemsWithNoWildcards.ContainsKey(fullPath))
                            {
                                // Another update will already happen on this path. Make that happen before evaluating this one.
                                addToBatch = false;
                                break;
                            }
                            else
                            {
                                itemsWithNoWildcards.Add(fullPath, op);
                            }
                        }
                        if (!addToBatch)
                        {
                            // We found a wildcard. Remove any fragments associated with the current operation and process them later.
                            for (int j = 0; j < i; j++)
                            {
                                itemsWithNoWildcards.Remove(currentList._memoizedOperation.Operation.Spec.Fragments[j].TextFragment);
                            }
                        }
                        else
                        {
                            addedToBatch = true;
                            continue;
                        }
                    }

                    if (addedToBatch)
                    {
                        addedToBatch = false;
                        ProcessNonWildCardItemUpdates(itemsWithNoWildcards, items);
                    }

                    // If this is a remove operation, then it could modify the globs to ignore, so pop the potentially
                    //  modified entry off the stack of globs to ignore
                    if (currentList._memoizedOperation.Operation is RemoveOperation)
                    {
                        globsToIgnoreStack.Pop();
                        currentGlobsToIgnore = globsToIgnoreStack.Count == 0 ? globsToIgnore : globsToIgnoreStack.Peek();
                    }

                    currentList._memoizedOperation.Apply(items, currentGlobsToIgnore);
                }

                // We finished looping through the operations. Now process the final batch if necessary.
                ProcessNonWildCardItemUpdates(itemsWithNoWildcards, items);

                return(items);
            }