dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.

Home Page:https://docs.microsoft.com/visualstudio/msbuild/msbuild

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Transitive package reference from project reference ignores Copy Local/Private setting for .Net Framework projects.

General-Fault opened this issue · comments

Issue Description

When referencing a project that itself contains a package reference, the "Copy Local" (<Private>False</Private>) setting is not honored for the package. Consequently, the parent project out directory contains all package assemblies even if the reference project assemblies are not copied.

Steps to Reproduce

Consider these two .Net Framework projects using the Sdk file format. ProjectA and ProjectB. ProjectB references ProjectA with Private="False", and ProjectA contains a PackageReference.

ProjectA.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="System.Text.Json" Version="9.0.0-preview.3.24172.9" />
  </ItemGroup>
</Project>

ProjectB.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
      <Private>False</Private>
    </ProjectReference>
  </ItemGroup>
</Project>

Note that the ProjectA contains a package reference to System.Text.Json.
Note that the ProjectB contains a "not private" project reference to ProjectA (Copy Local = False).

Expected Behavior

The output directory for ProjectB should only contain the output for that project, ProjectB.dll.

Actual Behavior

The output directory for ProjectB does not contain ProjectA.dll as expected. However, it does contain System.Text.Json.dll and all dependencies of System.Text.Json.

Analysis

I've tried all manner of using PrivateAssets and DisableTransitiveProjectReferences as described here:
https://github.com/dotnet/msbuild/blob/main/documentation/wiki/Controlling-Dependencies-Behavior.md#not-copying-projectreference

I've also attempted to set PrivateAssets within the ItemDefinitionGroup in ProjectB.

<ItemDefinitionGroup>
  <PackageReference>
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
</ItemDefinitionGroup>

Setting PrivateAssets to all for the package reference in ProjectA prevents the package from being copied to the output of ProjectB. But this is causes other problems for me as ProjectC (not included here) is an executable and must contain the full output of both ProjectA and ProjectB including all dependencies.

For reference here - in my application, ProjectB is a runtime discoverable "plug-in". I am trying to prevent polluting my "plugin" directory with assemblies that are loaded by the main application.

Versions & Configurations

MSBuild version 17.9.8+b34f75857 for .NET Framework
17.9.8.16306

Microsoft Visual Studio Professional 2022 (64-bit) - Current
Version 17.9.4

Is this a correct problem statement?: You want to influence copying of PackageReference items from the referenced project (through ProjectReference) within the referencing project. E.g. you want PackageReference items from ProjectA not to be copied to output of referencing ProjectB, but to be copied to output of referencing ProjectC.

If that's correct - I do not believe it's supported today (but I'll dig further) - but can you workaround by specifying PrivateAssets on the PackageReference items in ProjectA (so that they are excluded by referencing projects) and explicitly adding those to ProjectC (so that they are copied there)?

One workaround is to let ProjectB copy the assemblies to the output directory, but then copy the assemblies from there to a separate directory and exclude the indirect references from that copy. Microsoft.Build.Artifacts can be used for that.

Is this a correct problem statement?: You want to influence copying of PackageReference items from the referenced project (through ProjectReference) within the referencing project. E.g. you want PackageReference items from ProjectA not to be copied to output of referencing ProjectB, but to be copied to output of referencing ProjectC.

If that's correct - I do not believe it's supported today (but I'll dig further) - but can you workaround by specifying PrivateAssets on the PackageReference items in ProjectA (so that they are excluded by referencing projects) and explicitly adding those to ProjectC (so that they are copied there)?

Not quite... ProjectC in this case also has a reference to ProjectA and the necessary packages. In my use-case, ProjectA is a "common" assembly that includes shared interfaces. ProjectB is a runtime discoverable plugin. By the time the plugin is loaded by the ProjectC executable, the shared ProjectA.dll its dependencies have already been loaded. By this token, the PrivateAssets approach is not usable here.

What I'm trying to do here is have the output directory for ProjectB (and installer generated from those outputs) only contain assemblies that are specific to that plugin and not the "common" assemblies also used by Project A and ProjectC.

For what it's worth, I think I may have solved my own problem using <IncludeAssets>compile</IncludeAssets> in the ProjectReference tag in ProjectB. So, is the "bug" here only in my head where I simply didn't understand the intention of the MSBuild developers? Or should <Private>False</Private> imply that the dependencies of the referenced project also not be included in the output as I had expected?

@KalleOlaviNiemitalo Thank you for pointing me toward Microsoft.Build.Artifacts! This probably isn't the ideal solution for the project that led to me filing this issue. However, this may solve another related challenge I'm facing.

@General-Fault No - you are right, this is a known bug in MSBuild/sdk/nuget: dotnet/sdk#1366
Closing this as duplicate now