dotnet / dotnet/msbuild

Make a way for Target authors to easily clean up unused/intermediate Items after Target invocation

Open
#12,894 6 comments 1 reaction 0 assignees View on GitHub
Area: Engine Area: Language Area: Performance backlog triaged
Dominant language
C#
Stars
5.5k
Forks
1.5k
Avg merge
1d 8h
Merged PRs (30d)
141

Description

### Summary

Today it's common for authors of MSBuild Target logic to create Items in Targets for many reasons:

* need to store per-item metadata/computed values
* incrementally build up to a solution
* track specific steps through a complex filtering/sorting workflow

An example of this is the following code from MSBuildLocator:

```xml


<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Framework'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Utilities.Core'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Tasks.Core'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Engine'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Conversion.Core'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Runtime'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Localization'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.Build.Engine'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'Microsoft.NET.StringTools'))" />
<_MSBuildAssembliesCopyLocalItems Include="@(RuntimeCopyLocalItems->WithMetadataValue('NuGetPackageId', 'NuGet.Frameworks'))" />

<_DistinctMSBuildPackagesReferencedPoorly Include="@(MSBuildAssembliesCopyLocalItems->Metadata('NuGetPackageId')->Distinct())" />



```

Where a list of candidates is built up over time in progressive steps, then used to compute a list of PackageReferences that violate the MSBuild Locator packaging rules. In projects with many references this intermediate list can represent hundreds of Items.

These Items often represent discrete steps towards a final goal, but one that goal is accomplished they remain in the engine because there's no clear indicator in the engine if something is actually _consuming_ those items or not.

Today, users must remember to manually clean up these lists (which we haven't done at time of writing in Locator!).

While we can add [tooling](https://github.com/dotnet/msbuild/pull/12887) that warns users when this happens, we should have a more first-class way to annotate that items can be automatically cleaned up by the engine after Target invocation.

### Background and Motivation

* Improved memory usage of the build
* Reduced developer cognitive load

### Proposed Feature

There are a few ways of approaching this, I'll highlight a few in brief:

* The engine could track usage itself and have some logic for when to clean up Items
* Difficult to know for certain because Inputs and Outputs of Targets aren't comprehensive today - could lead to premature deletions
* The individual Items could support a metadata that signaled that they could be deleted after Target Invocation
* `<_MyThing Include="..." AutoRemove="true" />`
* Annoying because it's a bit manual, it makes the 'easy' thing the non-performant thing
* Metadata name is subject to collisions - if we choose a common name we alter semantics for existing code, if we choose an ugly-but-unique name it's hard for users to remember and use
* The Target could support declaring names of Item lists to remove after execution
* `...`
* semi-declarative, shows intent, relatively easily tool-able?

### Alternative Designs

_No response_

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.