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); }
protected virtual void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore) { var items = SelectItems(listBuilder, globsToIgnore); MutateItems(items); SaveItems(items, listBuilder); }
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); } }
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 }
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()); }
protected virtual void SaveItems(ImmutableList <I> items, OrderedItemDataCollection.Builder listBuilder) { }
protected virtual ImmutableList <I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore) { return(listBuilder.Select(itemData => itemData.Item) .ToImmutableList()); }
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(); } }
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); }