Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NoTargets] Can't build with BuildProjectReferences=false when referencing a multi-targeted project #427

Open
benjamin-hodgson opened this issue Mar 10, 2023 · 8 comments

Comments

@benjamin-hodgson
Copy link

benjamin-hodgson commented Mar 10, 2023

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.

@benjamin-hodgson benjamin-hodgson changed the title [NoTargets] Can't build with BuildProjectReferences=false when downstream of a multi-targeted project [NoTargets] Can't build with BuildProjectReferences=false when referencing a multi-targeted project Mar 10, 2023
@stan-sz
Copy link
Contributor

stan-sz commented Nov 23, 2023

Have you tried @benjamin-hodgson changing deployment.proj to deployment.csproj?

<LanguageTargets Condition=" '$(LanguageTargets)' == '' And '$(MSBuildProjectExtension)' != '.csproj' And '$(MSBuildProjectExtension)' != '.vbproj' And '$(MSBuildProjectExtension)' != '.fsproj' ">$(MSBuildToolsPath)\Microsoft.Common.targets</LanguageTargets>

@benjamin-hodgson
Copy link
Author

benjamin-hodgson commented Dec 1, 2023

@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)

@ViktorHofer
Copy link
Member

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.

@benjamin-hodgson
Copy link
Author

benjamin-hodgson commented Dec 5, 2023

@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.

@ViktorHofer
Copy link
Member

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.

@benjamin-hodgson
Copy link
Author

benjamin-hodgson commented Dec 5, 2023

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?

@benjamin-hodgson
Copy link
Author

benjamin-hodgson commented Dec 5, 2023

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)

@MattKotsenas
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants