dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.7k stars 1.06k forks source link

RuntimeIdentifier does not propagate to dependency projects when multi-targeting TFMs #10625

Open fredrikhr opened 4 years ago

fredrikhr commented 4 years ago

I have two projects, one is a ProjectReference dependency of the other. Both projects multi-target the same two TFMs (netcoreapp3.1 and net47) and I aim to do a RID-specific standalone deployment and therefore need to specify the RID at compile-time.

However, the build fails, appearently due to MSBuild not flowing the RuntimeIdentifier property when it is compiling the dependency project, thus resulting in a missing target dependency resolution error. NETSDK1047: Assets file does not have a target for the specified RID.

Steps to reproduce

  1. Produce a global.json file fixing the SDK to version 3.1.101 (current .NET Core SDK when filing the issue). The issue also repros with new preview versions (tested 3.1.200-preview-014883)
  2. Create two projects side-by-side
    dotnet new console -n dependency
    dotnet new console -n application
  3. Change both project files to a multi-targeting setup (this issue does not repro when single-targeting one TFM)
    <TargetFrameworks>netcoreapp3.1;net47</TargetFrameworks>
  4. Add a reference from one of the projects to the other.
    dotnet add application reference dependency
  5. Restore the appliction project with specifying a runtime identifier dotnet restore application --runtime win10-x64 -bl:MSBuild.Restore.binlog
  6. Build the application project with the same runtime identifier as before dotnet build application --runtime win10-x64 -bl:MSBuild.Build.binlog

Expected behaviour

The dotnet build application command should resolve all dependencies (i.e. ..\dependency\dependency.csproj) build and produce the binary assets for the application\application.csproj project in the application\bin\Debug\(net47)|(netcoreapp3.1)\win10-x64 directories. The Build should succeed. Specifically, the binary output for dependency should reflect the runtime identifier specification for win10-x64 as that was passed to the command-line arguments.

Current behaviour

The build fails:

[...]\dotnet\sdk\3.1.101\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(234,5): error NETSDK1047: Assets file '[...]\dependency\obj\project.assets.json' doesn't have a target for '.NETFramework,Version=v4.7/win7-x86'. Ensure that restore has run and that you have included 'net47' in the TargetFrameworks for your project. You may also need to include 'win7-x86' in your project's RuntimeIdentifiers. [[...]\dependency\dependency.csproj]

The error message states that there is a missing target for TFM net47 and RID win7-x86.

Attached ZIP file containing the project files, and the produced MSBuild binary logs:
net47-win10-x64-test.zip

Comments

The RID shown in the error message specifies win7-x86 which is a completely different RID compared to win10-x64 (different OS AND different architecture).

I have looked at the binary log (MSBuild.Build.binlog in the attached ZIP-file) and discovered that the ResolveReferences targets during the build of application.csproj invokes the MSBuild task to build the dependency. In the parameters passed to that task, the dependency.csproj project specifically gains an additional item property UndefineProperties which is set to ;RuntimeIdentifier. And sure enough, in the MSBuild invocation for dependency.csproj there is no Property RuntimeIdentifier set.

If the reference was a PackageReference or binary DLL that was RID-agnostic, the current behaviour would probably be fine. However, in the case when MSBuild recognizes that it needs to compile a dependency (i.e. when using ProjectReference) the RuntimeIdentifier property should flow with the compilation for the dependency. If the dependency is RID-agnostic, specifying a RID won't do any harm, but if it is RID-sensitive, specifying the RID is necessary.

zcsizmadia commented 4 years ago

Try to add the default runtime identifier to the "dependency" project. It seems the RuntimeIdentifier gets propagated to the dependency project, however when it is checked in the msbuild scripts, the RID is not set yet when compiling the dependent project. This worked for me in .NET Core and I was still able to override RID using dotnet build -r.

<PropertyGroup>
  <RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
jeremyVignelles commented 2 years ago

Huge thank you to both of you for putting me in the right track. I had a similar issue where I wanted an EXE project (let's name it A) to reference a library (no need to be platform-specific, B) that includes a project reference to another EXE (just to copy the executable in the output folder, C).

When I published A with -r linux-arm, a .exe file was put into the publised folder, which doesn't work on linux.

Here's what I did:

In project B:

  <PropertyGroup>
    <RuntimeIdentifiers>win-x64;linux-arm</RuntimeIdentifiers>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\C\C.csproj" ReferenceOutputAssembly="false" OutputItemType="None" CopyToOutputDirectory="PreserveNewest" Targets="Publish" />
  </ItemGroup>

In Project C:

  <PropertyGroup>
    <RuntimeIdentifiers>win-x64;linux-arm</RuntimeIdentifiers>
  </PropertyGroup>

I'm using RuntimeIndentifiers with a s after looking at the code here:

https://github.com/dotnet/msbuild/blob/3f82c6d5a1f6b8668a7c6122b240732139abaa57/src/Tasks/Microsoft.Common.CurrentVersion.targets#L1848-L1850

Asks for a IsRidAgnostic, which is declared here :

https://github.com/dotnet/msbuild/blob/b6e7d6051a3c3f595bfa11bd3b2749c0c8cc7f3c/src/Tasks/Microsoft.Common.CrossTargeting.targets#L48

Which is false if any of the RuntimeIdentifier(s) variable is set.