dotnet / dotnet/msbuild

Item to copy to output

Open
#10,106 9 comments 6 reactions 0 assignees View on GitHub
Area: Common Targets Area: Documentation Priority:2 triaged
Dominant language
C#
Stars
5.5k
Forks
1.5k
Avg merge
1d 13h
Merged PRs (30d)
133

Description

If we need to copy a file to output directory, the current pattern seems to be:

```xml


PreserveNewest

```

It works by first assigning the target path in the `AssignTargetPaths` (plural!) target. The output of the `AssignTargetPath` task (singular!) is copied to the `NoneWithTargetPath` item:
https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L3197-L3199

All the `AssignTargetPath` task does is append the `TargetPath` 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#L122

Then `NoneWithTargetPath` gets copied to `_ThisProjectItemsToCopyToOutputDirectory`:
https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5176-L5179

Then the target returns and the items go into `_ThisProjectItemsToCopyToOutputDirectory`:
https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5195-L5197

Then the items flow into `_SourceItemsToCopyToOutputDirectory`:
https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5204-L5214

Finally the copy happens in `_CopyOutOfDateSourceItemsToOutputDirectory`:
https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5264-L5282

Note the destination is `$(OutDir)%(TargetPath)`. This is what the `TargetPath` 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 the `None` 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 the `TargetPath` 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-L5174

Conveniently, `AssignTargetPath` is being called for this item, so it acquires the `TargetPath` 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.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.