-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathLazyItemEvaluator.RemoveOperation.cs
123 lines (104 loc) · 4.82 KB
/
LazyItemEvaluator.RemoveOperation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Build.Construction;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
#nullable disable
namespace Microsoft.Build.Evaluation
{
internal partial class LazyItemEvaluator<P, I, M, D>
{
private class RemoveOperation : LazyItemOperation
{
private readonly ImmutableList<string> _matchOnMetadata;
private MetadataTrie<P, I> _metadataSet;
public RemoveOperation(RemoveOperationBuilder builder, LazyItemEvaluator<P, I, M, D> lazyEvaluator)
: base(builder, lazyEvaluator)
{
_matchOnMetadata = builder.MatchOnMetadata.ToImmutable();
ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(
_matchOnMetadata.IsEmpty || _itemSpec.Fragments.All(f => f is ItemSpec<P, I>.ItemExpressionFragment),
new BuildEventFileInfo(string.Empty),
"OM_MatchOnMetadataIsRestrictedToReferencedItems");
if (_matchOnMetadata.Any())
{
_metadataSet = new MetadataTrie<P, I>(builder.MatchOnMetadataOptions, _matchOnMetadata, _itemSpec);
}
}
/// <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.Any();
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);
}
}
private bool MatchesItemOnMetadata(I item)
{
return _metadataSet.Contains(_matchOnMetadata.Select(m => item.GetMetadataValue(m)));
}
public ImmutableHashSet<string>.Builder GetRemovedGlobs()
{
var builder = ImmutableHashSet.CreateBuilder<string>();
if (!_conditionResult)
{
return builder;
}
var globs = _itemSpec.Fragments.OfType<GlobFragment>().Select(g => g.TextFragment);
builder.UnionWith(globs);
return builder;
}
}
private class RemoveOperationBuilder : OperationBuilder
{
public ImmutableList<string>.Builder MatchOnMetadata { get; } = ImmutableList.CreateBuilder<string>();
public MatchOnMetadataOptions MatchOnMetadataOptions { get; set; }
public RemoveOperationBuilder(ProjectItemElement itemElement, bool conditionResult) : base(itemElement, conditionResult)
{
}
}
}
}