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

buildTransitive behavior fails #12483

Closed
chrisdaiii opened this issue Mar 14, 2023 · 8 comments
Closed

buildTransitive behavior fails #12483

chrisdaiii opened this issue Mar 14, 2023 · 8 comments
Assignees
Labels
Area:ContentFiles PackageReference contentFiles folder Product:dotnet.exe Type:Bug

Comments

@chrisdaiii
Copy link

NuGet Product Used

Other/NA

Product Version

dotnet 7.0.2 Nuget 6.5.0.136

Worked before?

No response

Impact

It's more difficult to complete my work

Repro Steps & Context

Say I have the following project structure:

ProjectA
│
└── ProjectB
    │
    └── ProjectC
        │
        └── buildTransitive
        │    │
        │    └── ProjectC.props
        └── MyFiles
              │
              └── 1.txt   2.txt   3.txt

ProjectC.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramewrok>net6.0</TargetFramewrok>
  </PropertyGroup>
</Project>

<ItemGroup>
  <None Include="buildTransitive\**">
    <Pack>true</Pack>
    <PackagePath>buildTransitive</PackagePath>
  </None>

  <Content Include="MyFiles\**">
    <Pack>true</Pack>
    <PackageCopyToOutput>true</PackageCopyToOutput>
    <PackagePath>contentFiles\MyFiles</PackagePath>
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </Content >
</ItemGroup>

ProjectC.props:

<Project>
  <ItemGroup>
    <Content Include="$(MSBuildThisFileDirectory)..\contentFiles\MyFiles\**">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
    </Content>
  </ItemGroup>
</Project>

Both ProjectB and ProjectC are Nuget packages, and ProjectA only references ProjectB.

When my ProjectA is a Framework project (net48), the buildTransitive of the indirectly dependent ProjectC fails, and the content files will not be copied to the output directory of ProjectA, but when my ProjectA project is an SDK-style .net core When projecting, everything is normal.

I want to know why when the .net core project indirectly depends on the Nuget package, the dependency file will be copied to the output directory normally, but the .net Framework project will not. Thanks!

Verbose Logs

No response

@erdembayar
Copy link
Contributor

Thank you for filing this issue.
Could you be able to provide us small repro code for both cases? So, I can compare them?

@erdembayar erdembayar added WaitingForCustomer Applied when a NuGet triage person needs more info from the OP Product:dotnet.exe Area:ContentFiles PackageReference contentFiles folder and removed Triage:Untriaged labels Mar 14, 2023
@chrisdaiii
Copy link
Author

NugetBuildTransitive

Note: Both ProjectA-net6.0 and ProjectB-net48 refer to the local ProjectB Nuget package, please pack ProjectB and ProjectC into a directory, and modify the NuGet.config file at the same time.

Please help me to understand what is going on, thank you very much!

@ghost ghost added WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. and removed WaitingForCustomer Applied when a NuGet triage person needs more info from the OP labels Mar 15, 2023
@chrisdaiii
Copy link
Author

After you complete the above operations, compile the entire solution, you can see the ProjectA-net6.0\bin\Denug\net6.0\net6.0\1.txt file, but there is no ProjectA-net48\bin\Debug directory.

@erdembayar erdembayar removed the WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. label Mar 15, 2023
@erdembayar
Copy link
Contributor

After you complete the above operations, compile the entire solution, you can see the ProjectA-net6.0\bin\Denug\net6.0\net6.0\1.txt file, but there is no ProjectA-net48\bin\Debug directory.

I think it's by design, I'll check and let you know soon.

@chrisdaiii
Copy link
Author

After you complete the above operations, compile the entire solution, you can see the ProjectA-net6.0\bin\Denug\net6.0\net6.0\1.txt file, but there is no ProjectA-net48\bin\Debug directory.

I think it's by design, I'll check and let you know soon.

ok, thanks.

@erdembayar
Copy link
Contributor

Hey,
We don't think there's a NuGet bug here.
The props in the buildtransitive folder is getting included in the project which is where the NuGet responsibility ends.
The implementation of the props file is where the issue is.
I'm gonna close this issue, but I'm happy to help you improve the authoring of the props file.

TargetFramework is an opaque string, which is not something that's available in both SDK (or .NET projects) and old style csproj (.NET Framework projects). The .NET Framework project does not have TargetFramework, so it will never evaluate correctly.
I.e TargetFramework which is used in ProjectC.prop is not really thing for netframework(net48) unless you define it, you can read more from here.

For net6.0 dotnet case it understands TargetFramework because you defined it in line 3.

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <RootNamespace>ProjectA_Core</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

Since net4.8 netframework doesn't have TargetFramework, but it you can use TargetFrameworkMoniker, TargetFrameworkVersion instead.

<Project>
	<>
	<ItemGroup Condition="'$(TargetFrameworkMoniker)' == '.NETFramework,Version=v4.8'">
		<Content Include="$(MSBuildThisFileDirectory)..\contentFiles\any\net48\**">
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
			<Link>net48\%(Filename)%(Extension)</Link>
		</Content>
	</ItemGroup>

	<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
		<Content Include="$(MSBuildThisFileDirectory)..\contentFiles\any\net6.0\**">
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
			<Link>net6.0\%(Filename)%(Extension)</Link>
		</Content>
	</ItemGroup>
	
	<PropertyGroup>
		<OutputType>Library</OutputType>
	</PropertyGroup>
</Project>

image

But we don't recommend doing this way, because this kind of fragile implementation, it doesn't take into account of the package compatibility, next time if you want to extent above for net7.0 then you need to manually add it even though you don't need to. Probably you need to add for by platform too next time, like net7.0-android etc with above.

Instead, please use asset control (https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets).

  <ItemGroup>
    <PackageReference Include="ProjectB"  PrivateAssets="none" Version="1.0.0" />
    <PackageReference Include="ProjectY"  IncludeAssets="contentFiles" Version="2.0.0" />
    <PackageReference Include="ProjectZ"  IncludeAssets="build;buildTransitive" Version="3.0.0" />
  </ItemGroup>

Above is more flexible and scalable in long run, see more from here.

@erdembayar erdembayar self-assigned this Mar 16, 2023
@chrisdaiii
Copy link
Author

Thank you for helping me figure out what's wrong, thank you very much, wish you a good day every day.

@chrisdaiii
Copy link
Author

Hey, We don't think there's a NuGet bug here. The props in the buildtransitive folder is getting included in the project which is where the NuGet responsibility ends. The implementation of the props file is where the issue is. I'm gonna close this issue, but I'm happy to help you improve the authoring of the props file.

TargetFramework is an opaque string, which is not something that's available in both SDK (or .NET projects) and old style csproj (.NET Framework projects). The .NET Framework project does not have TargetFramework, so it will never evaluate correctly. I.e TargetFramework which is used in ProjectC.prop is not really thing for netframework(net48) unless you define it, you can read more from here.

For net6.0 dotnet case it understands TargetFramework because you defined it in line 3.

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <RootNamespace>ProjectA_Core</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

Since net4.8 netframework doesn't have TargetFramework, but it you can use TargetFrameworkMoniker, TargetFrameworkVersion instead.

<Project>
	<>
	<ItemGroup Condition="'$(TargetFrameworkMoniker)' == '.NETFramework,Version=v4.8'">
		<Content Include="$(MSBuildThisFileDirectory)..\contentFiles\any\net48\**">
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
			<Link>net48\%(Filename)%(Extension)</Link>
		</Content>
	</ItemGroup>

	<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
		<Content Include="$(MSBuildThisFileDirectory)..\contentFiles\any\net6.0\**">
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
			<Link>net6.0\%(Filename)%(Extension)</Link>
		</Content>
	</ItemGroup>
	
	<PropertyGroup>
		<OutputType>Library</OutputType>
	</PropertyGroup>
</Project>

image

But we don't recommend doing this way, because this kind of fragile implementation, it doesn't take into account of the package compatibility, next time if you want to extent above for net7.0 then you need to manually add it even though you don't need to. Probably you need to add for by platform too next time, like net7.0-android etc with above.

Instead, please use asset control (https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets).

  <ItemGroup>
    <PackageReference Include="ProjectB"  PrivateAssets="none" Version="1.0.0" />
    <PackageReference Include="ProjectY"  IncludeAssets="contentFiles" Version="2.0.0" />
    <PackageReference Include="ProjectZ"  IncludeAssets="build;buildTransitive" Version="3.0.0" />
  </ItemGroup>

Above is more flexible and scalable in long run, see more from here.

Sorry to bother you, I want to ask you a question, if I use asset control as you said (not applicable to .props files), then my ProjectA will not have 1.txt 2.txt 3.txt files, These files are all in ProjectC, ProjectB refers to ProjectC, and ProjectA refers to ProjectB.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area:ContentFiles PackageReference contentFiles folder Product:dotnet.exe Type:Bug
Projects
None yet
Development

No branches or pull requests

2 participants