-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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:
<Target Name="EnsureMSBuildAssembliesNotCopied" AfterTargets="ResolvePackageAssets" Condition="'$(DisableMSBuildAssemblyCopyCheck)' != 'true'">
<ItemGroup>
<_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'))" />
<MSBuildAssembliesCopyLocalItems Include="@(_MSBuildAssembliesCopyLocalItems->WithMetadataValue('CopyLocal', 'true')->WithMetadataValue('AssetType', 'runtime'))" />
<_DistinctMSBuildPackagesReferencedPoorly Include="@(MSBuildAssembliesCopyLocalItems->Metadata('NuGetPackageId')->Distinct())" />
</ItemGroup>
<Error
Condition="@(_DistinctMSBuildPackagesReferencedPoorly->Count()) > 0"
Code="MSBL001"
Text="A PackageReference to the package '%(_DistinctMSBuildPackagesReferencedPoorly.NuGetPackageId)' at version '%(_DistinctMSBuildPackagesReferencedPoorly.NuGetPackageVersion)' is present in this project without ExcludeAssets="runtime" and PrivateAssets="all" set. This can cause errors at run-time due to MSBuild assembly-loading."
HelpLink="https://aka.ms/msbuild/locator/diagnostics/MSBL001"
/>
</Target>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 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
<Target Name="DoStuff" Inputs="@(Things);@(Stuff)" Outputs="@(MyThings)" Locals="@(_MyIntermediates)">...</Target>- semi-declarative, shows intent, relatively easily tool-able?
Alternative Designs
No response