Some item operations explode the list. We have to provide alternative behavior that does not explode list.
The following comment is copy pasted from the evaluator:
///A ProjectItemElement could have resulted in several items if it contains wildcards or item or property expressions.
///Before any edit to a ProjectItem (remove, rename, set metadata, or remove metadata) this must be called to make
///sure that the edit does not affect any other ProjectItems originating in the same ProjectItemElement.
Project.SplitItemElementIfNecessary(ProjectItemElement itemElement)
Project.RemoveItem(ProjectItem) (1-2 weeks)
- Scenarios
- User deletes file via file system or via VS
- CPS reacts by calling RemoveItem on all ProjectItems with that value
- The current Project.RemoveItem(ProjectItem)
- Removes item inside the Include attribute
- Explodes the item tag if it evaluated to multiple items
- Modifications to not explode
- If Include contains only (globs and literal strings)
- If item is covered by glob, then don't do anything
- If item is covered by literal list, remove all the literals that have the same value as the item to remove
- Extra credit for removing literals that have properties inside of them (Include="foo$(prop)")
- Else (items and properties present: Include="*.a;@(AnItem);$(AProperty);3.a;2.a;3.a")
- Option1
- Apply previous behavior (explode)
- Justification
- If you don't explode and add an exclude, csproj becomes bloated over time as the user deletes elements
- CPS should call RemoveItem on the correct item tags (e.g. call RemoveItem on tags that reference the item directly via globs / literals, and not on the tags that indirectly reference that item)
- Option2
- Split the item tag in 2 tags:
- A tag with just the globs and the literals, and then recursivelly call RemoveItem on this tag
- A tag with the indirect references and add the item to remove in the exclude
- Option3
- Split the item tag in more tags:
- A tag with just the globs and the literals, and then recursivelly call RemoveItem on this tag
- Explode the items coming from indirect references
Add Exclude for Item (1 week) - CPS currently has own implementation. We could add our own so they don't have to implement it
- Scenarios
- User wants to exclude a file from the build
- CPS wants to add an exclude to implement project model semantics
- Inputs
- Overload1
- Item - the ProjectItem whose underlying ProjectItemElement needs to have the exclude applied to
- Overload2
- Element - the ProjectItemElement to append the exclude on
- directoryToIgnore - full path to a directory to ignore. The exclude attribute gets appended with "directoryToIgnore/_/_."
- Returns
- Same implementation as RemoveItem: true if item was present in the project
- Remarks
- Edits the underlying xml for the specified item to exclude the item's evaluated value / directory
- Does not do anything if the exclude tag already has an exclude containing it
``` c#
//exclude a single file, and you know it's corresponding ProjectItem
bool ExcludeItem(ProjectItem item)
//exclude a whole directory, and you know the corresponding ProjectItemElement where to add the exclude
bool ExcludeItem(ProjectItemElement element, string directoryToIgnore)
```
ProjectItem.(Set|Remove)metadata (TODO)
In the presence of globs, the new Item Update attribute should be used instead of these.
ProjectItem.Rename (~1-2 weeks)
- Modifications to not explode
- If Include contains only (globs and literal strings)
- Same as remove, but rename literals instead of delete.
- [?] what to do if old name is in glob but new name is not in glob?
- Option1: Add the new name that is not in glob as a literal
- Option2: don't do anything
- [?] what to do if literal incorporates a property
- Don't do anything
- Raise exception
- Explode the item out and rename, potentially breaking the build logic
- Else (items and properties present: Include="*.a;@(AnItem);$(AProperty);3.a;2.a;3.a")
- Option1 from RemoveItem
- Apply previous behavior (explode)
- Justification
- If you don't explode and add an exclude, csproj becomes bloated over time as the user deletes elements
- CPS should call RemoveItem on the correct item tags (e.g. call RemoveItem on tags that reference the item directly via globs / literals, and not on the tags that indirectly reference that item)
- Option3 from RemoveItem
- Split the item tag in more tags:
- A tag with just the globs and the literals, and then recursively call Rename on this tag
- Explode the items coming from indirect references and rename
Project.AddItem (TODO)
- Only works (does not explode) when:
- the existing item or item group has no condition
- the existing item tag has no excludes
- neither the item nor the considered item tag have metadata
Flag to propagate item operations instead of exploding (2 weeks)
- [?] do we want this?
- Recursively propagate the above operations on indirect references
- act on glob and literal lists
- Recursivelycall the same operation on referenced items
- explode referenced properties
- Do string magic inside the properties. Recursively, since properties can reference each other.
Most helpful comment
I'd say that the minimum work we need to do here is provide an option to just fail if the requested operation _would_ explode the glob--then a caller could reconsider and do something else. That wouldn't require any additional MSBuild language concepts, but would allow smarter behavior. Of course, it also makes the caller more complex.