diff options
author | Lluis Sanchez <llsan@microsoft.com> | 2018-04-04 12:47:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-04 12:47:55 +0300 |
commit | dee7ca0fe9c8d19a44371ac5a48a89f192462495 (patch) | |
tree | 8db156a0f6a585a055a5e610edd500564644bc1f /main/src/core | |
parent | 18fb664db81ca5b91c496124a12b71a566e2c9a5 (diff) | |
parent | 72fa73bf767c905c2235006c15ff17ec43cfaa91 (diff) |
Merge pull request #4325 from mono/dotnetcore-handle-duplicate-msbuild-items
[Core] Fix duplicate key restore error if duplicate files in project
Diffstat (limited to 'main/src/core')
3 files changed, 113 insertions, 6 deletions
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Collections/LookupTable.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Collections/LookupTable.cs new file mode 100644 index 0000000000..562db382ff --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Collections/LookupTable.cs @@ -0,0 +1,98 @@ +// +// LookupTable.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2018 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace MonoDevelop.Core.Collections +{ + /// <summary> + /// A dictionary that allows multiple items for a single key. + /// </summary> + class LookupTable<TKey, TElement> + { + Dictionary<TKey, (TElement Item, ImmutableList<TElement>.Builder List)> lookupItems = new Dictionary<TKey, (TElement Item, ImmutableList<TElement>.Builder List)> (); + + public void Add (TKey key, TElement item) + { + (TElement Item, ImmutableList<TElement>.Builder List) existingItem; + + if (lookupItems.TryGetValue (key, out existingItem)) { + if (existingItem.List == null) { + existingItem.List = ImmutableList.CreateBuilder<TElement> (); + existingItem.List.Add (existingItem.Item); + // Need to add the updated tuple back to the dictionary + // otherwise the list is not added. + lookupItems [key] = existingItem; + } + existingItem.List.Add (item); + } else { + lookupItems.Add (key, (item, null)); + } + } + + public IEnumerable<TElement> GetItems (TKey key) + { + (TElement Item, ImmutableList<TElement>.Builder List) existingItem; + + if (lookupItems.TryGetValue (key, out existingItem)) { + if (existingItem.List != null) { + return existingItem.List; + } else { + return GetSingleItemAsEnumerable (existingItem.Item); + } + } + + return Enumerable.Empty<TElement> (); + } + + IEnumerable<TElement> GetSingleItemAsEnumerable (TElement item) + { + yield return item; + } + + public bool Remove (TKey key, TElement item) + { + (TElement Item, ImmutableList<TElement>.Builder List) existingItem; + + if (lookupItems.TryGetValue (key, out existingItem)) { + if (existingItem.List != null) { + existingItem.List.Remove (item); + if (!existingItem.List.Any ()) { + lookupItems.Remove (key); + } + } else { + lookupItems.Remove (key); + } + return true; + } + + return false; + } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index b9d56be636..df63c58266 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -762,6 +762,7 @@ <Compile Include="MonoDevelop.Core\StringBuilderCache.cs" /> <Compile Include="MonoDevelop.Core\ObjectPool.cs" /> <Compile Include="MonoDevelop.Core\SharedPools.cs" /> + <Compile Include="MonoDevelop.Core.Collections\LookupTable.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs index 8d0e626dd4..81781f78c8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs @@ -45,6 +45,7 @@ using MonoDevelop.Projects.Extensions; using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis; +using MonoDevelop.Core.Collections; namespace MonoDevelop.Projects { @@ -2931,15 +2932,15 @@ namespace MonoDevelop.Projects loadedItems.Clear (); HashSet<ProjectItem> unusedItems = null; - Dictionary<(string Name, string Include), ProjectItem> lookupItems = null; + LookupTable<(string Name, string Include), ProjectItem> lookupItems = null; ImmutableList<ProjectItem>.Builder newItems = null; if (IsReevaluating) { unusedItems = new HashSet<ProjectItem> (Items); - lookupItems = new Dictionary<(string Name, string Include), ProjectItem> (); + lookupItems = new LookupTable<(string Name, string Include), ProjectItem> (); newItems = ImmutableList.CreateBuilder<ProjectItem> (); // Improve ReadItem performance by creating a dictionary of items that can be - // searched faster than using Items.FirstOrDefault. Building this dictionary takes ~15ms + // searched faster than using Items.FirstOrDefault. Building this dictionary takes ~17ms foreach (var it in Items) { if (it.BackingItem != null && it.BackingEvalItem != null) { lookupItems.Add (GetProjectItemLookupKey (it.BackingEvalItem), it); @@ -3006,12 +3007,14 @@ namespace MonoDevelop.Projects productVersion = FileFormat.DefaultProductVersion; } - internal (ProjectItem Item, bool IsNew) ReadItem (IMSBuildItemEvaluated buildItem, Dictionary<(string Name, string Include), ProjectItem> lookupItems) + internal (ProjectItem Item, bool IsNew) ReadItem (IMSBuildItemEvaluated buildItem, LookupTable<(string Name, string Include), ProjectItem> lookupItems) { if (IsReevaluating) { // If this item already exists in the current collection of items, reuse it - if (lookupItems.TryGetValue (GetProjectItemLookupKey (buildItem), out ProjectItem eit)) { - if (ItemsAreEqual (buildItem, eit.BackingEvalItem) || CheckProjectReferenceItemsAreEqual (buildItem, eit)) { + var key = GetProjectItemLookupKey (buildItem); + foreach (var eit in lookupItems.GetItems (key)) { + if (ItemsAreEqual (buildItem, eit)) { + lookupItems.Remove (key, eit); eit.BackingItem = buildItem.SourceItem; eit.BackingEvalItem = buildItem; return (eit, false); @@ -3047,6 +3050,11 @@ namespace MonoDevelop.Projects return (item, true); } + bool ItemsAreEqual (IMSBuildItemEvaluated buildItem, ProjectItem item) + { + return ItemsAreEqual (buildItem, item.BackingEvalItem) || CheckProjectReferenceItemsAreEqual (buildItem, item); + } + /// <summary> /// Special case ProjectReference items when checking for a match for ReadItem. The underlying build /// items may not have matching metadata properties but the ProjectReference.Equals method may |