msbuildmsbuild-target

Can a ProjectReference provide custom MSBuild targets to consumers?


The docs say:

NuGet packages may sometimes add custom build targets or properties to projects that consume that package. This can be achieved by adding a valid MSBuild file, in the form <package_id>.targets or <package_id>.props within the build folders of the project.

While developing a library that provides custom build targets, I'd like to reference it via ProjectReference so I don't have to repackage it to test the consumer after every change.

My test library is called Magic. It contains a target file named Magic.targets in the project root.

<Project>
    <Target Name="CustomTarget" DependsOnTargets="Build">
        <Message Importance="high" Text="fnord"/>
    </Target>
</Project>

This file is not automatically copied to bin/Debug/netstandard2.0. If I set its properties to copy, it appears under bin but not in the corresponding location under obj. It is not referenced in obj/Magic.csproj.nuget.g.targets. No message is printed when I build the consuming project. I also tried <Warning Text="fnord"/> with no effect.

I'm developing in Rider in case that makes a difference.


Solution

  • No, a project can't receive custom targets via a ProjectReference.

    An MSBuild project can reference another project with ProjectReference and reference a NuGet package with PackageReference. But they are different mechanisms.

    ProjectReference is much older than PackageReference. NuGet's initial release was 7 years after MSBuild's. ProjectReference doesn't know about NuGet, doesn't know about <package_id>, and doesn't import MSBuild files from the referenced project.

    MSBuild processes a project in two phases: evaluation followed by execution. Imports are handled in the evaluation phase. ProjectReferences are processed and resolved in the execution phase when the build target is invoked. PackageReferences are processed in the execution phase when the restore target is invoked.

    Files that only become available in the execution phase can't be Imported. It's too late. This is part of why msbuild -t:restore,build can produce "incorrect behavior" and msbuild -t:build -restore is recommended. The -restore option essentially runs the restore target, then forces a reload of the project (with a new evaluation) before running the targets in the -t option.