RDK0007 — Output copies MSBuild does not know about
Severity: warning · Command: redecker inspect
A package copies files into the output directory without recording them in @(FileWrites).
The mechanism
MSBuild tracks what a build produced through the FileWrites item. IncrementalClean uses that list to remove outputs a previous build produced and the current one no longer does. Clean uses it to know what to delete. Both are working from a ledger.
A Copy task that does not feed its CopiedFiles back into FileWrites writes files that are not in the ledger:
<!-- MSBuild has no idea this happened -->
<Copy SourceFiles="@(NativeFiles)" DestinationFolder="$(OutDir)" />The fix is one element:
<Copy SourceFiles="@(NativeFiles)" DestinationFolder="$(OutDir)">
<Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
</Copy>Why it bites .NET Framework hardest
net4x predates the runtime asset resolution that makes native-asset copying automatic on .NET Core. A package shipping native binaries to net48 therefore has to hand-roll the copy in its own targets — and the accounting is the easy part to leave out.
The symptoms are the awkward kind rather than a clean failure:
- files that survive a
Clean, because nothing knows they are there - files deleted by
IncrementalCleanand copied again on every build - up-to-date checks that disagree with what is actually on disk
The tell
A package that ships its own clean target is strong corroboration:
<Target Name="CleanSNIFiles" ...>
<Delete Files="@(SNIFiles -> '$(OutDir)%(RecursiveDir)%(Filename)%(Extension)')" ... />
</Target>You only need to hand-roll Clean because MSBuild was never told what you wrote. The rule says so when it sees one.
Real case
Microsoft.Data.SqlClient.SNI 6.0.2 does exactly this in its net462 targets — see evidence.
$ redecker inspect Microsoft.Data.SqlClient.SNI --to 6.0.2
warning RDK0007: build/net462/Microsoft.Data.SqlClient.SNI.targets copies 2 time(s) into the
output directory without recording FileWritesWhat it ignores
Copies to anywhere that is not build output — $(IntermediateOutputPath), a staging folder, a tool's own working directory. IncrementalClean has no opinion about those, so neither does this rule. It looks only at $(OutDir), $(OutputPath), $(TargetDir) and $(PublishDir).
Accounting done in a sibling ItemGroup is accepted too:
<Copy SourceFiles="@(N)" DestinationFolder="$(OutDir)" />
<ItemGroup>
<FileWrites Include="@(N -> '$(OutDir)%(Filename)%(Extension)')" />
</ItemGroup>Less direct, same ledger.
Why a warning
Because the consequences depend on target ordering and on what the consuming build does. This is a hazard and a maintenance smell rather than a guaranteed break, and calling it an error would overstate what can be known from the package alone.