Adding merging of ProjectItemElement.Update. Before, we only merged Include.
This commit is contained in:
parent
55d5d98332
commit
3bbfcb428b
4 changed files with 346 additions and 87 deletions
|
@ -71,6 +71,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
|
|
||||||
public const string IncludesNotEquivalent = "{0}.{1} includes not equivalent.";
|
public const string IncludesNotEquivalent = "{0}.{1} includes not equivalent.";
|
||||||
|
|
||||||
|
public const string UpdatesNotEquivalent = "{0}.{1} updates not equivalent.";
|
||||||
|
|
||||||
public const string ExcludesNotEquivalent = "{0}.{1} excludes not equivalent.";
|
public const string ExcludesNotEquivalent = "{0}.{1} excludes not equivalent.";
|
||||||
|
|
||||||
public const string RemovesNotEquivalent = "{0}.{1} removes not equivalent.";
|
public const string RemovesNotEquivalent = "{0}.{1} removes not equivalent.";
|
||||||
|
@ -109,6 +111,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
|
|
||||||
public const string ItemTransformApplicatorEncompassedIncludes = "{0}: encompassed includes {1}";
|
public const string ItemTransformApplicatorEncompassedIncludes = "{0}: encompassed includes {1}";
|
||||||
|
|
||||||
|
public const string ItemTransformApplicatorEncompassedUpdates = "{0}: encompassed updates {1}";
|
||||||
|
|
||||||
public const string ItemTransformApplicatorRemovingItem = "{0}: Removing Item {{ ItemType: {1}, Condition: {2}, Include: {3}, Exclude: {4} }}";
|
public const string ItemTransformApplicatorRemovingItem = "{0}: Removing Item {{ ItemType: {1}, Condition: {2}, Include: {3}, Exclude: {4} }}";
|
||||||
|
|
||||||
public const string ItemTransformApplicatorIgnoringItem = "{0}: Ignoring Item {{ ItemType: {1}, Condition: {2}, Include: {3}, Exclude: {4} }}";
|
public const string ItemTransformApplicatorIgnoringItem = "{0}: Ignoring Item {{ ItemType: {1}, Condition: {2}, Include: {3}, Exclude: {4} }}";
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
public static IEnumerable<string> GetEncompassedIncludes(this ProjectItemElement item,
|
public static IEnumerable<string> GetEncompassedIncludes(this ProjectItemElement item,
|
||||||
ProjectItemElement otherItem, TextWriter trace = null)
|
ProjectItemElement otherItem, TextWriter trace = null)
|
||||||
{
|
{
|
||||||
if (otherItem.IsEquivalentToExceptIncludeAndExclude(item, trace) &&
|
if (otherItem.IsEquivalentToExceptIncludeUpdateAndExclude(item, trace) &&
|
||||||
new HashSet<string>(otherItem.Excludes()).IsSubsetOf(new HashSet<string>(item.Excludes())))
|
new HashSet<string>(otherItem.Excludes()).IsSubsetOf(new HashSet<string>(item.Excludes())))
|
||||||
{
|
{
|
||||||
return otherItem.IntersectIncludes(item);
|
return otherItem.IntersectIncludes(item);
|
||||||
|
@ -23,6 +23,18 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return Enumerable.Empty<string>();
|
return Enumerable.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> GetEncompassedUpdates(this ProjectItemElement item,
|
||||||
|
ProjectItemElement otherItem, TextWriter trace = null)
|
||||||
|
{
|
||||||
|
if (otherItem.IsEquivalentToExceptIncludeUpdateAndExclude(item, trace) &&
|
||||||
|
new HashSet<string>(otherItem.Excludes()).IsSubsetOf(new HashSet<string>(item.Excludes())))
|
||||||
|
{
|
||||||
|
return otherItem.IntersectUpdates(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsEquivalentTo(this ProjectItemElement item, ProjectItemElement otherItem, TextWriter trace = null)
|
public static bool IsEquivalentTo(this ProjectItemElement item, ProjectItemElement otherItem, TextWriter trace = null)
|
||||||
{
|
{
|
||||||
// Different includes
|
// Different includes
|
||||||
|
@ -32,6 +44,12 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.IntersectUpdates(otherItem).Count() != item.Updates().Count())
|
||||||
|
{
|
||||||
|
trace?.WriteLine(String.Format(LocalizableStrings.UpdatesNotEquivalent, nameof(MSBuildExtensions), nameof(IsEquivalentTo)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Different Excludes
|
// Different Excludes
|
||||||
if (item.IntersectExcludes(otherItem).Count() != item.Excludes().Count())
|
if (item.IntersectExcludes(otherItem).Count() != item.Excludes().Count())
|
||||||
{
|
{
|
||||||
|
@ -39,10 +57,10 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return item.IsEquivalentToExceptIncludeAndExclude(otherItem, trace);
|
return item.IsEquivalentToExceptIncludeUpdateAndExclude(otherItem, trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsEquivalentToExceptIncludeAndExclude(this ProjectItemElement item, ProjectItemElement otherItem, TextWriter trace = null)
|
public static bool IsEquivalentToExceptIncludeUpdateAndExclude(this ProjectItemElement item, ProjectItemElement otherItem, TextWriter trace = null)
|
||||||
{
|
{
|
||||||
// Different remove
|
// Different remove
|
||||||
if (item.Remove != otherItem.Remove)
|
if (item.Remove != otherItem.Remove)
|
||||||
|
@ -119,6 +137,12 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return SplitSemicolonDelimitedValues(item.Include);
|
return SplitSemicolonDelimitedValues(item.Include);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> Updates(
|
||||||
|
this ProjectItemElement item)
|
||||||
|
{
|
||||||
|
return SplitSemicolonDelimitedValues(item.Update);
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<string> Excludes(
|
public static IEnumerable<string> Excludes(
|
||||||
this ProjectItemElement item)
|
this ProjectItemElement item)
|
||||||
{
|
{
|
||||||
|
@ -141,6 +165,11 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return item.Includes().Intersect(otherItem.Includes());
|
return item.Includes().Intersect(otherItem.Includes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> IntersectUpdates(this ProjectItemElement item, ProjectItemElement otherItem)
|
||||||
|
{
|
||||||
|
return item.Updates().Intersect(otherItem.Updates());
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<string> IntersectExcludes(this ProjectItemElement item, ProjectItemElement otherItem)
|
public static IEnumerable<string> IntersectExcludes(this ProjectItemElement item, ProjectItemElement otherItem)
|
||||||
{
|
{
|
||||||
return item.Excludes().Intersect(otherItem.Excludes());
|
return item.Excludes().Intersect(otherItem.Excludes());
|
||||||
|
@ -151,6 +180,11 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
item.Include = string.Join(";", item.Includes().Except(includesToRemove));
|
item.Include = string.Join(";", item.Includes().Except(includesToRemove));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RemoveUpdates(this ProjectItemElement item, IEnumerable<string> updatesToRemove)
|
||||||
|
{
|
||||||
|
item.Update = string.Join(";", item.Updates().Except(updatesToRemove));
|
||||||
|
}
|
||||||
|
|
||||||
public static void UnionIncludes(this ProjectItemElement item, IEnumerable<string> includesToAdd)
|
public static void UnionIncludes(this ProjectItemElement item, IEnumerable<string> includesToAdd)
|
||||||
{
|
{
|
||||||
item.Include = string.Join(";", item.Includes().Union(includesToAdd));
|
item.Include = string.Join(";", item.Includes().Union(includesToAdd));
|
||||||
|
|
|
@ -19,12 +19,18 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
{
|
{
|
||||||
if (typeof(T) != typeof(ProjectItemElement))
|
if (typeof(T) != typeof(ProjectItemElement))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(String.Format(LocalizableStrings.ExpectedElementToBeOfTypeNotTypeError, nameof(ProjectItemElement), typeof(T)));
|
throw new ArgumentException(String.Format(
|
||||||
|
LocalizableStrings.ExpectedElementToBeOfTypeNotTypeError,
|
||||||
|
nameof(ProjectItemElement),
|
||||||
|
typeof(T)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof(U) != typeof(ProjectItemGroupElement))
|
if (typeof(U) != typeof(ProjectItemGroupElement))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(String.Format(LocalizableStrings.ExpectedElementToBeOfTypeNotTypeError, nameof(ProjectItemGroupElement), typeof(U)));
|
throw new ArgumentException(String.Format(
|
||||||
|
LocalizableStrings.ExpectedElementToBeOfTypeNotTypeError,
|
||||||
|
nameof(ProjectItemGroupElement),
|
||||||
|
typeof(U)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element == null)
|
if (element == null)
|
||||||
|
@ -40,8 +46,18 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
var item = element as ProjectItemElement;
|
var item = element as ProjectItemElement;
|
||||||
var destinationItemGroup = destinationElement as ProjectItemGroupElement;
|
var destinationItemGroup = destinationElement as ProjectItemGroupElement;
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorHeader, nameof(ItemTransformApplicator), item.ItemType, item.Condition, item.Include, item.Exclude, item.Update));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorItemGroup, nameof(ItemTransformApplicator), destinationItemGroup.Condition));
|
LocalizableStrings.ItemTransformApplicatorHeader,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
item.ItemType,
|
||||||
|
item.Condition,
|
||||||
|
item.Include,
|
||||||
|
item.Exclude,
|
||||||
|
item.Update));
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorItemGroup,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
destinationItemGroup.Condition));
|
||||||
|
|
||||||
if (mergeExisting)
|
if (mergeExisting)
|
||||||
{
|
{
|
||||||
|
@ -49,7 +65,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
item = MergeWithExistingItemsWithSameCondition(item, destinationItemGroup);
|
item = MergeWithExistingItemsWithSameCondition(item, destinationItemGroup);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformAppliatorItemCompletelyMerged, nameof(ItemTransformApplicator)));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformAppliatorItemCompletelyMerged,
|
||||||
|
nameof(ItemTransformApplicator)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,14 +75,18 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
item = MergeWithExistingItemsWithNoCondition(item, destinationItemGroup);
|
item = MergeWithExistingItemsWithNoCondition(item, destinationItemGroup);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformAppliatorItemCompletelyMerged, nameof(ItemTransformApplicator)));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformAppliatorItemCompletelyMerged,
|
||||||
|
nameof(ItemTransformApplicator)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
item = MergeWithExistingItemsWithACondition(item, destinationItemGroup);
|
item = MergeWithExistingItemsWithACondition(item, destinationItemGroup);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformAppliatorItemCompletelyMerged, nameof(ItemTransformApplicator)));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformAppliatorItemCompletelyMerged,
|
||||||
|
nameof(ItemTransformApplicator)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,13 +110,22 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
var outputItem = itemGroup.ContainingProject.CreateItemElement("___TEMP___");
|
var outputItem = itemGroup.ContainingProject.CreateItemElement("___TEMP___");
|
||||||
outputItem.CopyFrom(item);
|
outputItem.CopyFrom(item);
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorAddItemHeader, nameof(ItemTransformApplicator), outputItem.ItemType, outputItem.Condition, outputItem.Include, outputItem.Exclude, outputItem.Update));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorAddItemHeader,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
outputItem.ItemType,
|
||||||
|
outputItem.Condition,
|
||||||
|
outputItem.Include,
|
||||||
|
outputItem.Exclude,
|
||||||
|
outputItem.Update));
|
||||||
|
|
||||||
itemGroup.AppendChild(outputItem);
|
itemGroup.AppendChild(outputItem);
|
||||||
outputItem.AddMetadata(item.Metadata, MigrationTrace.Instance);
|
outputItem.AddMetadata(item.Metadata, MigrationTrace.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProjectItemElement MergeWithExistingItemsWithACondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
private ProjectItemElement MergeWithExistingItemsWithACondition(
|
||||||
|
ProjectItemElement item,
|
||||||
|
ProjectItemGroupElement destinationItemGroup)
|
||||||
{
|
{
|
||||||
// This logic only applies to conditionless items
|
// This logic only applies to conditionless items
|
||||||
if (item.ConditionChain().Any() || destinationItemGroup.ConditionChain().Any())
|
if (item.ConditionChain().Any() || destinationItemGroup.ConditionChain().Any())
|
||||||
|
@ -105,63 +136,156 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
var existingItemsWithACondition =
|
var existingItemsWithACondition =
|
||||||
FindExistingItemsWithACondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
FindExistingItemsWithACondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorMergingItemWithExistingItems, nameof(ItemTransformApplicator), existingItemsWithACondition.Count()));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorMergingItemWithExistingItems,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
existingItemsWithACondition.Count()));
|
||||||
|
|
||||||
foreach (var existingItem in existingItemsWithACondition)
|
foreach (var existingItem in existingItemsWithACondition)
|
||||||
{
|
{
|
||||||
// If this item is encompassing items in a condition, remove the encompassed includes from the existing item
|
if (!string.IsNullOrEmpty(item.Include))
|
||||||
var encompassedIncludes = item.GetEncompassedIncludes(existingItem, MigrationTrace.Instance);
|
|
||||||
if (encompassedIncludes.Any())
|
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorEncompassedIncludes, nameof(ItemTransformApplicator), string.Join(", ", encompassedIncludes)));
|
MergeOnIncludesWithExistingItemsWithACondition(item, existingItem, destinationItemGroup);
|
||||||
existingItem.RemoveIncludes(encompassedIncludes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue if the existing item is now empty
|
if (!string.IsNullOrEmpty(item.Update))
|
||||||
if (!existingItem.Includes().Any())
|
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorRemovingItem, nameof(ItemTransformApplicator), existingItem.ItemType, existingItem.Condition, existingItem.Include, existingItem.Exclude));
|
MergeOnUpdatesWithExistingItemsWithACondition(item, existingItem, destinationItemGroup);
|
||||||
existingItem.Parent.RemoveChild(existingItem);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we haven't continued, the existing item may have includes
|
|
||||||
// that need to be removed before being redefined, to avoid duplicate includes
|
|
||||||
// Create or merge with existing remove
|
|
||||||
var remainingIntersectedIncludes = existingItem.IntersectIncludes(item);
|
|
||||||
|
|
||||||
if (remainingIntersectedIncludes.Any())
|
|
||||||
{
|
|
||||||
var existingRemoveItem = destinationItemGroup.Items
|
|
||||||
.Where(i =>
|
|
||||||
string.IsNullOrEmpty(i.Include)
|
|
||||||
&& string.IsNullOrEmpty(i.Exclude)
|
|
||||||
&& !string.IsNullOrEmpty(i.Remove))
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (existingRemoveItem != null)
|
|
||||||
{
|
|
||||||
var removes = new HashSet<string>(existingRemoveItem.Remove.Split(';'));
|
|
||||||
foreach (var include in remainingIntersectedIncludes)
|
|
||||||
{
|
|
||||||
removes.Add(include);
|
|
||||||
}
|
|
||||||
existingRemoveItem.Remove = string.Join(";", removes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
|
||||||
clearPreviousItem.Remove = string.Join(";", remainingIntersectedIncludes);
|
|
||||||
|
|
||||||
AddItemToItemGroup(clearPreviousItem, existingItem.Parent as ProjectItemGroupElement);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProjectItemElement MergeWithExistingItemsWithNoCondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
private void MergeOnIncludesWithExistingItemsWithACondition(
|
||||||
|
ProjectItemElement item,
|
||||||
|
ProjectItemElement existingItem,
|
||||||
|
ProjectItemGroupElement destinationItemGroup)
|
||||||
|
{
|
||||||
|
// If this item is encompassing items in a condition, remove the encompassed includes from the existing item
|
||||||
|
var encompassedIncludes = item.GetEncompassedIncludes(existingItem, MigrationTrace.Instance);
|
||||||
|
if (encompassedIncludes.Any())
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorEncompassedIncludes,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
string.Join(", ", encompassedIncludes)));
|
||||||
|
existingItem.RemoveIncludes(encompassedIncludes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue if the existing item is now empty
|
||||||
|
if (!existingItem.Includes().Any())
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorRemovingItem,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
existingItem.ItemType,
|
||||||
|
existingItem.Condition,
|
||||||
|
existingItem.Include,
|
||||||
|
existingItem.Exclude));
|
||||||
|
existingItem.Parent.RemoveChild(existingItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we haven't continued, the existing item may have includes
|
||||||
|
// that need to be removed before being redefined, to avoid duplicate includes
|
||||||
|
// Create or merge with existing remove
|
||||||
|
var remainingIntersectedIncludes = existingItem.IntersectIncludes(item);
|
||||||
|
|
||||||
|
if (remainingIntersectedIncludes.Any())
|
||||||
|
{
|
||||||
|
var existingRemoveItem = destinationItemGroup.Items
|
||||||
|
.Where(i =>
|
||||||
|
string.IsNullOrEmpty(i.Include)
|
||||||
|
&& string.IsNullOrEmpty(i.Exclude)
|
||||||
|
&& !string.IsNullOrEmpty(i.Remove))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (existingRemoveItem != null)
|
||||||
|
{
|
||||||
|
var removes = new HashSet<string>(existingRemoveItem.Remove.Split(';'));
|
||||||
|
foreach (var include in remainingIntersectedIncludes)
|
||||||
|
{
|
||||||
|
removes.Add(include);
|
||||||
|
}
|
||||||
|
existingRemoveItem.Remove = string.Join(";", removes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
||||||
|
clearPreviousItem.Remove = string.Join(";", remainingIntersectedIncludes);
|
||||||
|
|
||||||
|
AddItemToItemGroup(clearPreviousItem, existingItem.Parent as ProjectItemGroupElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MergeOnUpdatesWithExistingItemsWithACondition(
|
||||||
|
ProjectItemElement item,
|
||||||
|
ProjectItemElement existingItem,
|
||||||
|
ProjectItemGroupElement destinationItemGroup)
|
||||||
|
{
|
||||||
|
// If this item is encompassing items in a condition, remove the encompassed updates from the existing item
|
||||||
|
var encompassedUpdates = item.GetEncompassedUpdates(existingItem, MigrationTrace.Instance);
|
||||||
|
if (encompassedUpdates.Any())
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorEncompassedUpdates,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
string.Join(", ", encompassedUpdates)));
|
||||||
|
existingItem.RemoveIncludes(encompassedUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue if the existing item is now empty
|
||||||
|
if (!existingItem.Updates().Any())
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorRemovingItem,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
existingItem.ItemType,
|
||||||
|
existingItem.Condition,
|
||||||
|
existingItem.Update,
|
||||||
|
existingItem.Exclude));
|
||||||
|
existingItem.Parent.RemoveChild(existingItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we haven't continued, the existing item may have updates
|
||||||
|
// that need to be removed before being redefined, to avoid duplicate updates
|
||||||
|
// Create or merge with existing remove
|
||||||
|
var remainingIntersectedUpdates = existingItem.IntersectUpdates(item);
|
||||||
|
|
||||||
|
if (remainingIntersectedUpdates.Any())
|
||||||
|
{
|
||||||
|
var existingRemoveItem = destinationItemGroup.Items
|
||||||
|
.Where(i =>
|
||||||
|
string.IsNullOrEmpty(i.Update)
|
||||||
|
&& string.IsNullOrEmpty(i.Exclude)
|
||||||
|
&& !string.IsNullOrEmpty(i.Remove))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (existingRemoveItem != null)
|
||||||
|
{
|
||||||
|
var removes = new HashSet<string>(existingRemoveItem.Remove.Split(';'));
|
||||||
|
foreach (var update in remainingIntersectedUpdates)
|
||||||
|
{
|
||||||
|
removes.Add(update);
|
||||||
|
}
|
||||||
|
existingRemoveItem.Remove = string.Join(";", removes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
||||||
|
clearPreviousItem.Remove = string.Join(";", remainingIntersectedUpdates);
|
||||||
|
|
||||||
|
AddItemToItemGroup(clearPreviousItem, existingItem.Parent as ProjectItemGroupElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProjectItemElement MergeWithExistingItemsWithNoCondition(
|
||||||
|
ProjectItemElement item,
|
||||||
|
ProjectItemGroupElement destinationItemGroup)
|
||||||
{
|
{
|
||||||
// This logic only applies to items being placed into a condition
|
// This logic only applies to items being placed into a condition
|
||||||
if (!item.ConditionChain().Any() && !destinationItemGroup.ConditionChain().Any())
|
if (!item.ConditionChain().Any() && !destinationItemGroup.ConditionChain().Any())
|
||||||
|
@ -172,22 +296,67 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
var existingItemsWithNoCondition =
|
var existingItemsWithNoCondition =
|
||||||
FindExistingItemsWithNoCondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
FindExistingItemsWithNoCondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorMergingItemWithExistingItems, nameof(ItemTransformApplicator), existingItemsWithNoCondition.Count()));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorMergingItemWithExistingItems,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
existingItemsWithNoCondition.Count()));
|
||||||
|
|
||||||
// Handle the item being placed inside of a condition, when it is overlapping with a conditionless item
|
if (!string.IsNullOrEmpty(item.Include))
|
||||||
// If it is not definining new metadata or excludes, the conditioned item can be merged with the
|
|
||||||
// conditionless item
|
|
||||||
foreach (var existingItem in existingItemsWithNoCondition)
|
|
||||||
{
|
{
|
||||||
var encompassedIncludes = existingItem.GetEncompassedIncludes(item, MigrationTrace.Instance);
|
// Handle the item being placed inside of a condition, when it is overlapping with a conditionless item
|
||||||
if (encompassedIncludes.Any())
|
// If it is not definining new metadata or excludes, the conditioned item can be merged with the
|
||||||
|
// conditionless item
|
||||||
|
foreach (var existingItem in existingItemsWithNoCondition)
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorEncompassedIncludes, nameof(ItemTransformApplicator), string.Join(", ", encompassedIncludes)));
|
var encompassedIncludes = existingItem.GetEncompassedIncludes(item, MigrationTrace.Instance);
|
||||||
item.RemoveIncludes(encompassedIncludes);
|
if (encompassedIncludes.Any())
|
||||||
if (!item.Includes().Any())
|
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorIgnoringItem, nameof(ItemTransformApplicator), existingItem.ItemType, existingItem.Condition, existingItem.Include, existingItem.Exclude));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
return null;
|
LocalizableStrings.ItemTransformApplicatorEncompassedIncludes,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
string.Join(", ", encompassedIncludes)));
|
||||||
|
item.RemoveIncludes(encompassedIncludes);
|
||||||
|
if (!item.Includes().Any())
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorIgnoringItem,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
existingItem.ItemType,
|
||||||
|
existingItem.Condition,
|
||||||
|
existingItem.Include,
|
||||||
|
existingItem.Exclude));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(item.Update))
|
||||||
|
{
|
||||||
|
// Handle the item being placed inside of a condition, when it is overlapping with a conditionless item
|
||||||
|
// If it is not definining new metadata or excludes, the conditioned item can be merged with the
|
||||||
|
// conditionless item
|
||||||
|
foreach (var existingItem in existingItemsWithNoCondition)
|
||||||
|
{
|
||||||
|
var encompassedUpdates = existingItem.GetEncompassedUpdates(item, MigrationTrace.Instance);
|
||||||
|
if (encompassedUpdates.Any())
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorEncompassedUpdates,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
string.Join(", ", encompassedUpdates)));
|
||||||
|
item.RemoveUpdates(encompassedUpdates);
|
||||||
|
if (!item.Updates().Any())
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorIgnoringItem,
|
||||||
|
nameof(ItemTransformApplicator),
|
||||||
|
existingItem.ItemType,
|
||||||
|
existingItem.Condition,
|
||||||
|
existingItem.Update,
|
||||||
|
existingItem.Exclude));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,18 +369,20 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
var existingRemoveItem = destinationItemGroup.Items
|
var existingRemoveItem = destinationItemGroup.Items
|
||||||
.Where(i =>
|
.Where(i =>
|
||||||
string.IsNullOrEmpty(i.Include)
|
string.IsNullOrEmpty(i.Include)
|
||||||
|
&& string.IsNullOrEmpty(i.Update)
|
||||||
&& string.IsNullOrEmpty(i.Exclude)
|
&& string.IsNullOrEmpty(i.Exclude)
|
||||||
&& !string.IsNullOrEmpty(i.Remove))
|
&& !string.IsNullOrEmpty(i.Remove))
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
var itemsToRemove = string.IsNullOrEmpty(item.Include) ? item.Update : item.Include;
|
||||||
if (existingRemoveItem != null)
|
if (existingRemoveItem != null)
|
||||||
{
|
{
|
||||||
existingRemoveItem.Remove += ";" + item.Include;
|
existingRemoveItem.Remove += ";" + itemsToRemove;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
||||||
clearPreviousItem.Remove = item.Include;
|
clearPreviousItem.Remove = itemsToRemove;
|
||||||
|
|
||||||
AddItemToItemGroup(clearPreviousItem, destinationItemGroup);
|
AddItemToItemGroup(clearPreviousItem, destinationItemGroup);
|
||||||
}
|
}
|
||||||
|
@ -220,12 +391,19 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProjectItemElement MergeWithExistingItemsWithSameCondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
private ProjectItemElement MergeWithExistingItemsWithSameCondition(
|
||||||
|
ProjectItemElement item,
|
||||||
|
ProjectItemGroupElement destinationItemGroup)
|
||||||
{
|
{
|
||||||
var existingItemsWithSameCondition =
|
var existingItemsWithSameCondition = FindExistingItemsWithSameCondition(
|
||||||
FindExistingItemsWithSameCondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
item,
|
||||||
|
destinationItemGroup.ContainingProject,
|
||||||
|
destinationItemGroup);
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorMergingItemWithExistingItemsSameChain, nameof(TransformApplicator), existingItemsWithSameCondition.Count()));
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorMergingItemWithExistingItemsSameChain,
|
||||||
|
nameof(TransformApplicator),
|
||||||
|
existingItemsWithSameCondition.Count()));
|
||||||
|
|
||||||
foreach (var existingItem in existingItemsWithSameCondition)
|
foreach (var existingItem in existingItemsWithSameCondition)
|
||||||
{
|
{
|
||||||
|
@ -238,7 +416,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
existingItem.Parent.RemoveChild(existingItem);
|
existingItem.Parent.RemoveChild(existingItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine(String.Format(LocalizableStrings.ItemTransformApplicatorAddingMergedItem,
|
MigrationTrace.Instance.WriteLine(String.Format(
|
||||||
|
LocalizableStrings.ItemTransformApplicatorAddingMergedItem,
|
||||||
nameof(TransformApplicator),
|
nameof(TransformApplicator),
|
||||||
mergeResult.MergedItem.ItemType,
|
mergeResult.MergedItem.ItemType,
|
||||||
mergeResult.MergedItem.Condition,
|
mergeResult.MergedItem.Condition,
|
||||||
|
@ -250,6 +429,21 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MergeResult MergeItems(ProjectItemElement item, ProjectItemElement existingItem)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(item.Include))
|
||||||
|
{
|
||||||
|
return MergeItemsOnIncludes(item, existingItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(item.Update))
|
||||||
|
{
|
||||||
|
return MergeItemsOnUpdates(item, existingItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException(LocalizableStrings.CannotMergeItemsWithoutCommonIncludeError);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Merges two items on their common sets of includes.
|
/// Merges two items on their common sets of includes.
|
||||||
/// The output is 3 items, the 2 input items and the merged items. If the common
|
/// The output is 3 items, the 2 input items and the merged items. If the common
|
||||||
|
@ -262,18 +456,13 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
///
|
///
|
||||||
/// This function will mutate the Include property of the 2 input items, removing the common subset.
|
/// This function will mutate the Include property of the 2 input items, removing the common subset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private MergeResult MergeItems(ProjectItemElement item, ProjectItemElement existingItem)
|
private MergeResult MergeItemsOnIncludes(ProjectItemElement item, ProjectItemElement existingItem)
|
||||||
{
|
{
|
||||||
if (!string.Equals(item.ItemType, existingItem.ItemType, StringComparison.Ordinal))
|
if (!string.Equals(item.ItemType, existingItem.ItemType, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(LocalizableStrings.CannotMergeItemsOfDifferentTypesError);
|
throw new InvalidOperationException(LocalizableStrings.CannotMergeItemsOfDifferentTypesError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!item.IntersectIncludes(existingItem).Any())
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(LocalizableStrings.CannotMergeItemsWithoutCommonIncludeError);
|
|
||||||
}
|
|
||||||
|
|
||||||
var commonIncludes = item.IntersectIncludes(existingItem).ToList();
|
var commonIncludes = item.IntersectIncludes(existingItem).ToList();
|
||||||
var mergedItem = _projectElementGenerator.AddItem(item.ItemType, string.Join(";", commonIncludes));
|
var mergedItem = _projectElementGenerator.AddItem(item.ItemType, string.Join(";", commonIncludes));
|
||||||
|
|
||||||
|
@ -295,6 +484,36 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
return mergeResult;
|
return mergeResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MergeResult MergeItemsOnUpdates(ProjectItemElement item, ProjectItemElement existingItem)
|
||||||
|
{
|
||||||
|
if (!string.Equals(item.ItemType, existingItem.ItemType, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(LocalizableStrings.CannotMergeItemsOfDifferentTypesError);
|
||||||
|
}
|
||||||
|
|
||||||
|
var commonUpdates = item.IntersectUpdates(existingItem).ToList();
|
||||||
|
var mergedItem = _projectElementGenerator.AddItem(item.ItemType, "placeholder");
|
||||||
|
mergedItem.Include = string.Empty;
|
||||||
|
mergedItem.Update = string.Join(";", commonUpdates);
|
||||||
|
|
||||||
|
mergedItem.UnionExcludes(existingItem.Excludes());
|
||||||
|
mergedItem.UnionExcludes(item.Excludes());
|
||||||
|
|
||||||
|
mergedItem.AddMetadata(MergeMetadata(existingItem.Metadata, item.Metadata), MigrationTrace.Instance);
|
||||||
|
|
||||||
|
item.RemoveUpdates(commonUpdates);
|
||||||
|
existingItem.RemoveUpdates(commonUpdates);
|
||||||
|
|
||||||
|
var mergeResult = new MergeResult
|
||||||
|
{
|
||||||
|
InputItem = string.IsNullOrEmpty(item.Update) ? null : item,
|
||||||
|
ExistingItem = string.IsNullOrEmpty(existingItem.Update) ? null : existingItem,
|
||||||
|
MergedItem = mergedItem
|
||||||
|
};
|
||||||
|
|
||||||
|
return mergeResult;
|
||||||
|
}
|
||||||
|
|
||||||
private ICollection<ProjectMetadataElement> MergeMetadata(
|
private ICollection<ProjectMetadataElement> MergeMetadata(
|
||||||
ICollection<ProjectMetadataElement> existingMetadataElements,
|
ICollection<ProjectMetadataElement> existingMetadataElements,
|
||||||
ICollection<ProjectMetadataElement> newMetadataElements)
|
ICollection<ProjectMetadataElement> newMetadataElements)
|
||||||
|
@ -335,7 +554,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
.Where(i => i.Condition == item.Condition)
|
.Where(i => i.Condition == item.Condition)
|
||||||
.Where(i => i.Parent.ConditionChainsAreEquivalent(destinationContainer))
|
.Where(i => i.Parent.ConditionChainsAreEquivalent(destinationContainer))
|
||||||
.Where(i => i.ItemType == item.ItemType)
|
.Where(i => i.ItemType == item.ItemType)
|
||||||
.Where(i => i.IntersectIncludes(item).Any());
|
.Where(i => i.IntersectIncludes(item).Any() ||
|
||||||
|
i.IntersectUpdates(item).Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ProjectItemElement> FindExistingItemsWithNoCondition(
|
private IEnumerable<ProjectItemElement> FindExistingItemsWithNoCondition(
|
||||||
|
@ -346,7 +566,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
return project.Items
|
return project.Items
|
||||||
.Where(i => !i.ConditionChain().Any())
|
.Where(i => !i.ConditionChain().Any())
|
||||||
.Where(i => i.ItemType == item.ItemType)
|
.Where(i => i.ItemType == item.ItemType)
|
||||||
.Where(i => i.IntersectIncludes(item).Any());
|
.Where(i => i.IntersectIncludes(item).Any() ||
|
||||||
|
i.IntersectUpdates(item).Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ProjectItemElement> FindExistingItemsWithACondition(
|
private IEnumerable<ProjectItemElement> FindExistingItemsWithACondition(
|
||||||
|
@ -357,7 +578,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
return project.Items
|
return project.Items
|
||||||
.Where(i => i.ConditionChain().Any())
|
.Where(i => i.ConditionChain().Any())
|
||||||
.Where(i => i.ItemType == item.ItemType)
|
.Where(i => i.ItemType == item.ItemType)
|
||||||
.Where(i => i.IntersectIncludes(item).Any());
|
.Where(i => i.IntersectIncludes(item).Any() ||
|
||||||
|
i.IntersectUpdates(item).Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MergeResult
|
private class MergeResult
|
||||||
|
|
|
@ -189,7 +189,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
||||||
}",
|
}",
|
||||||
testDirectory: testDirectory);
|
testDirectory: testDirectory);
|
||||||
|
|
||||||
mockProj.Items.Count(i => i.ItemType.Equals("Content", StringComparison.Ordinal)).Should().Be(4);
|
mockProj.Items.Count(i => i.ItemType.Equals("Content", StringComparison.Ordinal)).Should().Be(3);
|
||||||
|
|
||||||
// From ProjectReader #L725 (Both are empty)
|
// From ProjectReader #L725 (Both are empty)
|
||||||
var defaultIncludePatterns = Enumerable.Empty<string>();
|
var defaultIncludePatterns = Enumerable.Empty<string>();
|
||||||
|
@ -198,11 +198,10 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
||||||
foreach (var item in mockProj.Items.Where(i => i.ItemType.Equals("Content", StringComparison.Ordinal)))
|
foreach (var item in mockProj.Items.Where(i => i.ItemType.Equals("Content", StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
var metadata = string.Join(",", item.Metadata.Select(m => m.Name));
|
var metadata = string.Join(",", item.Metadata.Select(m => m.Name));
|
||||||
Console.WriteLine($"LICAVALC: Update: {item.Update}, Include: {item.Include}, Metadata: {metadata}");
|
|
||||||
|
|
||||||
if (item.Update.Contains(@"root\**\*"))
|
if (item.Update.Contains(@"root\**\*"))
|
||||||
{
|
{
|
||||||
item.Update.Should().Be(@"root\**\*;src\**\*;rootfile.cs");
|
item.Update.Should().Be(@"root\**\*");
|
||||||
item.Exclude.Should().BeEmpty();
|
item.Exclude.Should().BeEmpty();
|
||||||
}
|
}
|
||||||
else if (item.Update.Contains(@"src\file1.cs"))
|
else if (item.Update.Contains(@"src\file1.cs"))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue