What Is the FileWrites Item Group in MSBuild Incremental Builds?
The FileWrites item group is a built-in MSBuild mechanism that records every file created by custom targets, enabling dotnet clean to remove generated files and ensuring incremental build consistency.
When building .NET projects, custom targets that generate code, assets, or intermediate files must track their outputs to maintain build hygiene. According to the dotnet/skills repository, the FileWrites item group serves as the official registry for these generated artifacts, bridging the gap between custom build logic and MSBuild's incremental build system.
What Is the FileWrites Item Group?
The FileWrites item group is a built-in MSBuild collection that tracks every file produced by custom targets during a build. As documented in plugins/dotnet-msbuild/skills/incremental-build/SKILL.md (lines 102-108), any file written by a target that isn't automatically tracked by MSBuild should be registered here to ensure proper cleanup and incremental behavior.
The item group works alongside FileWritesShareable, which handles files shared across multiple projects. While standard FileWrites entries are deleted during clean operations, shareable files are tracked but preserved if other projects still reference them.
Why FileWrites Matters for Incremental Builds
Clean Build Support
Without FileWrites registration, dotnet clean cannot remove generated files, leaving stale artifacts in output and intermediate folders. The plugins/dotnet-msbuild/skills/msbuild-antipatterns/references/incremental-build-inputs-outputs.md file (lines 27-30) highlights this as a critical requirement: generated files left behind can confuse subsequent builds and cause compilation errors from outdated inputs.
Incremental Build Correctness
When a target writes files not declared in its Outputs attribute, MSBuild may skip the target during incremental checks, yet downstream targets still depend on those files. Registering outputs in FileWrites ensures the build state remains consistent even when targets produce side effects outside their declared inputs/outputs contract.
How to Register Files in FileWrites
The standard pattern requires adding an ItemGroup inside the same target that generates the file. According to plugins/dotnet-msbuild/skills/including-generated-files/SKILL.md (lines 53-59), this registration must happen immediately after file creation.
<Target Name="MyGenerator"
Inputs="@(SomeInputs)"
Outputs="$(IntermediateOutputPath)generated.cs"
BeforeTargets="CoreCompile">
<WriteLinesToFile File="$(IntermediateOutputPath)generated.cs"
Lines="@(GeneratedLines)" />
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)generated.cs" />
</ItemGroup>
</Target>
For targets generating compile-time code:
<Target Name="GenerateVersionInfo"
Inputs="$(MSBuildProjectFile)"
Outputs="$(IntermediateOutputPath)VersionInfo.cs"
BeforeTargets="CoreCompile">
<WriteLinesToFile File="$(IntermediateOutputPath)VersionInfo.cs"
Lines="public static class VersionInfo { public const string Build = "$(Version)"; }" />
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)VersionInfo.cs" />
<Compile Include="$(IntermediateOutputPath)VersionInfo.cs" />
</ItemGroup>
</Target>
Handling Shared Generated Files
When multiple projects reference the same generated file, use FileWritesShareable instead of FileWrites. This prevents the file from being deleted during clean if another project still requires it.
<Target Name="GenerateSharedHelpers"
Outputs="$(IntermediateOutputPath)SharedHelpers.cs">
<!-- generation logic -->
<ItemGroup>
<FileWritesShareable Include="$(IntermediateOutputPath)SharedHelpers.cs" />
</ItemGroup>
</Target>
Internal Cleanup Mechanisms
The MSBuild engine uses internal targets to manage orphaned FileWrites entries. The plugins/dotnet-msbuild/skills/item-management/SKILL.md file (lines 161-163) references a helper target that removes stale entries:
<Target Name="_CleanOrphanFileWrites"
DependsOnTargets="_CleanPriorFileWrites">
<ItemGroup>
<_CleanOrphanFileWrites Include="@(_CleanPriorFileWrites)"
Exclude="@(_CleanCurrentFileWrites)" />
</ItemGroup>
<Delete Files="@(_CleanOrphanFileWrites)" />
</Target>
This ensures that files previously registered but no longer generated are properly cleaned up.
Summary
FileWritesis a built-in MSBuild item group that tracks files created by custom targets.- Registering generated files enables
dotnet cleanto remove them, preventing stale artifacts. - The
plugins/dotnet-msbuild/skills/incremental-build/SKILL.mddocumentation defines the registration pattern as adding anItemGroupwithFileWritesentries inside the generating target. - Incremental correctness depends on registering side-effect files that aren't declared in target
Outputs. - Use
FileWritesShareablefor files referenced across multiple projects to prevent premature deletion. - The
plugins/dotnet-msbuild/skills/item-management/SKILL.mdidentifies missingFileWritesregistration as a common antipattern in MSBuild projects.
Frequently Asked Questions
What happens if I don't register generated files in FileWrites?
If you omit FileWrites registration, dotnet clean cannot delete the generated files, leaving stale artifacts in your obj and bin folders. According to the dotnet/skills source analysis, these orphaned files can cause compilation errors in subsequent builds when the compiler picks up outdated generated code.
When should I use FileWritesShareable instead of FileWrites?
Use FileWritesShareable when a generated file is shared across multiple projects in a solution. While standard FileWrites entries are always deleted during clean, shareable files are only removed when no other project references them, preventing build breaks in dependent projects.
Can I register FileWrites outside the target that creates the file?
You should always register FileWrites inside the same target that generates the file, immediately after the file creation task. This ensures MSBuild correctly associates the file with the target execution and maintains accurate incremental build timestamps.
Does FileWrites affect incremental build skipping behavior?
While FileWrites primarily supports clean operations, it indirectly affects incremental correctness by documenting side effects. MSBuild skips targets based on Inputs and Outputs timestamps, but registering files in FileWrites ensures the build system recognizes these artifacts exist, preventing "up-to-date" false positives when generating files outside the standard outputs contract.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →