Open KirillOsenkov opened 6 months ago
also the benefit of using this is that it flows transitively to referencing projects. For example things like <Reference Include="..." Private="true" />
do not flow transitively. Would be nice to have control over the transitive behavior as well (copy to output of this project vs. copy to output for all referencing projects too)
My recommendation would be to literally clone _CompileItemsToCopy
, name it something sensible like CopyToOutput
and set the default metadata CopyToOutputDirectory=PreserveNewest
.
The biggest risk here is someone already having an item named CopyToOutput, not sure what to do in this case. I guess we'll just break them?
If we need to copy a file to output directory, the current pattern seems to be:
It works by first assigning the target path in the
AssignTargetPaths
(plural!) target. The output of theAssignTargetPath
task (singular!) is copied to theNoneWithTargetPath
item: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L3197-L3199All the
AssignTargetPath
task does is append theTargetPath
metadata to the item, usually just the name. It is the relative path inside the project output directory where to copy the file. Without it, you'll get an error saying the destination file path is a directory (because the target file path is empty). https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/AssignTargetPath.cs#L122Then
NoneWithTargetPath
gets copied to_ThisProjectItemsToCopyToOutputDirectory
: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5176-L5179Then the target returns and the items go into
_ThisProjectItemsToCopyToOutputDirectory
: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5195-L5197Then the items flow into
_SourceItemsToCopyToOutputDirectory
: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5204-L5214Finally the copy happens in
_CopyOutOfDateSourceItemsToOutputDirectory
: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5264-L5282Note the destination is
$(OutDir)%(TargetPath)
. This is what theTargetPath
metadata was needed for.======
Now, the problem with the
None
item is that it's considered an input by the Visual Studio Fast Up-To-Date Check. So if you are generating an item as part of the project build, and then add it to theNone
item to ensure it gets copied, you have a situation where the project's output is also its input, so the FUTDC will always consider the project not up-to-date, because the generated file was written to after the primary output assembly, but it's now an input, so we have an input newer than output.I was looking for a loophole to find a better way to do this. I first tried to directly add to the
_ThisProjectItemsToCopyToOutputDirectory
item, but without theTargetPath
metadata I got an error from the copy task because the destination file name was empty.The only thing I found that works is instead of
None
to add it to_CompileItemsToCopy
: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5163-L5174Conveniently,
AssignTargetPath
is being called for this item, so it acquires theTargetPath
metadata.However, obviously, it's a hack.
I wonder what's the official blessed pattern for ensuring that a file generated by this project gets copied to output. If we don't have one, we should make one and make it easy and fool-proof.