TL;DR
Is ContentWithTargetPath
actually just the internal MSBuild representation of <Content ..><TargetPath>..
or is this really a "proper" MSBuild configuration item?
Only one (1!) of several 100s of our projects contains an entry
<ContentWithTargetPath Include="..\x-model\v6\stuff.json">
<Link>data\stuff.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>stuff.json</TargetPath>
</ContentWithTargetPath>
I tried to find any docs for this, but while it is mentioned many times on S.O. here, there does not appear to be any documentation for this MSBuild Item.
What I further found was a mention on one github issue https://github.com/dotnet/msbuild/issues/2795 where ContentWithTargetPath
is mentioned together with _NoneWithTargetPath
.
I also found the Common MSBuild project items reference where the TargetPath
optional sub-entry is listed for <Content ...>
(but not for <None ...>
, even though None also seems to support it).
Also, as far as I can tell, <TargetPath>
in conjunction with CopyToOutputDirectory
behaves exactly the same, regardless of whether I use None
, Content
, or ContentWithTargetPath
as element in my actual csproj file.
Can anyone shed some light on why I can find over 50 entries of this MSBuild property here on S.O. but nowhere documented on the MS docs for MSBuild or even prominently in any dotnet github issues? Is there any history to this configuration item?
TL;DR Use Content
in your project files, use ContentWithTargetPath
when you need to run custom build logic as part of your build that executes after the AssignTargetPaths
target and you missed your chance of your Content
items being picked up.
Unfortunately, a lot of parts in the build process are not fully documented and can only be understood by looking at the actual "code" in MSBuild / the .NET SDK that ships the necessary logic.
As for ContentWithTargetPath
, it is not prefixed with an underscore and thus you can expect that people will try not to make breaking changes to code that creates or uses it.
Internally, ContentWithTargetPath
items get calculated during the build process since Content
does not need to contain a TargetPath
or Link
metadata by default so there is a step that creates an intermediate item that later steps in the build process can rely on - so no other logic (copy-to-output / publish) needs to implement calculating a target path themselves and there is a shared logic to rely on the TargetPath
metdata existing on these ContentWithTargetPath
items.
There is not much use in documenting ContentWithTargetPath
since you can achieve most things with the Content
item when authoring your projects and usually only advanced build customizations need to understand the more internal parts of the build process (for which you'd have to already read the built-in logic anyway). Also this means when people only use Content
tooling (e.g. different IDEs) needs to focus on understanding fewer items and not try to be smart about when to use which.
There is a small caveat in that after the ContentWithTargetPath
items are created from Content
items, build logic running after that step (AssignTargetPaths
target) have to create ContentWithTargetPath
items if they want to contribute items that need to be copied/published. This can be the case if you want to hook logic into dotnet publish
that adds additional items to the publish output but which does not need to run on dotnet build
for example.