[NoTargets] Can't build with BuildProjectReferences=false when referencing a multi-targeted project
Summary
When referencing a multi-targeted project from a NoTargets project, you can't build with BuildProjectReferences=false.
Repro
library\library.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net472</TargetFrameworks>
</PropertyGroup>
</Project>
deployment\deployment.proj:
<Project Sdk="Microsoft.Build.NoTargets">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\library\library.csproj" />
</ItemGroup>
</Project>
>dotnet build deployment -p:BuildProjectReferences=false
MSBuild version 17.5.0-preview-23061-01+040e2a90e for .NET
Determining projects to restore...
All projects are up-to-date for restore.
..\library\library.csproj : error MSB4057: The target "GetTargetPath" does not exist in the project.
Build FAILED.
..\library\library.csproj : error MSB4057: The target "GetTargetPath" does not exist in the project.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.80
Other details
Setting SkipGetTargetFrameworkProperties=false on the ProjectReference fixes the problem. The culprit appears to be this line, but I'm not sure what the impact of removing that would be.
This came up because QuickBuild (MS internal build tool for large repos) invokes MSBuild with BuildProjectReferences=false.
Have you tried @benjamin-hodgson changing deployment.proj to deployment.csproj?
https://github.com/microsoft/MSBuildSdks/blob/b54b4a19dd55281bd87e2f8dd482d444c44dc666/src/NoTargets/Sdk/Sdk.targets#L18
@stan-sz Doesn't help - happens with both .proj and .csproj. (NB: the NoTargets project is not multitargeted; it's the upstream project that's multitargeted)
The NoTargets SDK by default doesn't reference output assemblies of dependencies. You can change that behavior by setting NoTargetsDoNotReferenceOutputAssemblies=false.
That's by design as a NoTargets SDK doesn't require output assemblies from dependencies as it doesn't invoke the compiler.
@ViktorHofer I know that. I'm not expecting any particular behaviour regarding the assemblies themselves but I do expect to be able to run a build in this example.
Then you probably also understand that the error happens because the GetTargetPath target is tried to be invoked on the outer build which doesn't have that target as outer builds don't return a single path to a target (assembly).
SkipGetTargetFrameworkProperties was chosen to be set because years back, the NoTargets SDK didn't import the Microsoft.NET.Sdk and didn't require a TFM to be set. If we would now change that default, we would require an "appropriate" TFM to be set for NuGet to correctly calculate the (inner build node) dependency graph.
Changing that default would definitely be a major breaking change for NoTargets msbuild SDK consumers. AFAIK referencing a multi-targeting project from a NoTargets SDK project isn't the norm. There is also the Traversal SDK which has some overlap with the NoTargets SDK.
referencing a multi-targeting project from a NoTargets SDK project isn't the norm
What makes you say that?
Perhaps it'd help for me to explain our use-case: we use NoTargets together with Artifacts to pull together a bunch of assemblies (and other artifacts) into a deployment artifact to be pushed out to production machines. (We're not publishing a single application - it's a collection of libraries and apps which make up an environment for running user-supplied code.) So a typical NoTargets project looks roughly like:
<Project Sdk="Microsoft.Build.NoTargets">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- ensure libraries are built before we deploy them -->
<ProjectReference Include="path/to/library1.csproj" />
<ProjectReference Include="path/to/library2.csproj" />
<PackageReference Include="SomePackage" GeneratePathProperty="true" />
</ItemGroup>
<ItemGroup>
<Artifact Include="path/to/library1/bin/release/net472/library1.dll" />
<Artifact Include="path/to/library2/bin/release/net472/library2.dll" />
<Artifact Include="$(PkgSomePackage)/lib/netstandard2.0/SomePackage.dll" />
</ItemGroup>
</Project>
We build these artifacts in a variety of different configurations for a variety of different environments, including different .NET versions, so there are a number of such NoTargets projects. Many of the upstream projects are multitargeted.
So for my purposes I don't really care whether the output assemblies are referenced or whether TF negotiation happens at all, I'm just using the ProjectReferences to ensure that I'm at the right spot in the build chain. (If you have any other suggestions of how to achieve this then I'm interested in hearing them.)
I understand that requiring the TargetFramework to be compatible with the upstream projects (by default) would be a breaking change. On the other hand this is already required for normal csproj projects so NoTargets is the odd one out here, and it's easy to opt out via SkipGetTargetFrameworkProperties/SetTargetFramework as required. (And NoTargets already broke away from the old CoreXT tools by requiring a TargetFramework at all.)
Either way, from the perspective of an SDK user (especially one who is not a build expert) this does look like a legit bug - a normal-looking project fails to build with an arcane error message mentioning an internal target. Would it at least be an option to emit a better error message?
PS I haven't got a minimal repro right now but I suspect this bug also affects transitive project references to multitargeted projects (harder to work around on the downstream end)
I'm also going to push back against NoTargets projects referencing multitarget projects being uncommon.
My NoTargets project intends to ensure dependent projects are up-to-date (but doesn't care about compatibility), and then will create a NuGet package using the outputs.
What I think I really want is dotnet/msbuild#4795, and for that to work when used inside a NoTargets project.