dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.22k stars 1.36k forks source link

ProjectReference transitive dependencies #4717

Open joeltankam opened 5 years ago

joeltankam commented 5 years ago

Steps to reproduce

Let's consider these 3 project files :

ProjectA

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net471</TargetFramework>
  </PropertyGroup>
</Project>

ProjectB

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

ProjectC

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net471</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\ProjectB\ProjectB.csproj" />
  </ItemGroup>
</Project>

The 3 projects depends on each other as follows : ProjectC -(depends on)-> ProjectB -> ProjectA

Expected behavior

ProjectC should not be able to use elements from ProjectA (public classes for example) since it doesn't reference this project directly.

Actual behavior

ProjectC has access to ProjectA via transitivity as it can be seen in his assets file :

{
      ...
      "ProjectA/1.0.0": {
        "type": "project",
        "framework": ".NETFramework,Version=v4.7.1",
        "compile": {
          "bin/placeholder/ProjectA.dll": {}
        },
        "runtime": {
          "bin/placeholder/ProjectA.dll": {}
        }
      },
      "ProjectB/1.0.0": {
        "type": "project",
        "framework": ".NETFramework,Version=v4.7.1",
        "dependencies": {
          "ProjectA": "1.0.0"
        },
        "compile": {
          "bin/placeholder/ProjectB.dll": {}
        },
        "runtime": {
          "bin/placeholder/ProjectB.dll": {}
        }
      }
      ...
}

As mentioned in https://github.com/dotnet/project-system/issues/2313, it appears that this behavior can be cancelled by using PrivateAssets, from PackageReference dependency assets, when referencing ProjectA :

  <ItemGroup>
    <ProjectReference Include="..\ProjectA\ProjectA.csproj" PrivateAssets="all" />
  </ItemGroup>

Using ExcludeAssets when referencing ProjectB also works :

  <ItemGroup>
    <ProjectReference Include="..\ProjectB\ProjectB.csproj" ExcludeAssets="all" />
  </ItemGroup>

However, these metadatas are originally applicable only for PackageReference and I can't find any documentation of this behavior for ProjectReference (in the ProjectReference item reference for example). So, should this (PrivateAssets or ExcludeAsssets) be really used in ProjectReference tag ?

Environment data

msbuild /version output: 15.9.21.664 Visual Studio : Professional 2017, version 15.9.15

joeltankam commented 5 years ago

Any update on the topic please ?

livarcocc commented 5 years ago

You could use <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> to turn this off for Project to project references.

joeltankam commented 5 years ago

Ok, thanks. I'll try that.

joeltankam commented 5 years ago

@livarcocc <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> works fine as far as compilation is concerned. However, ProjectA is still present in ProjectC asset file (same as mentioned in the description); which is a false information since this dependency doesn't exist anymore at compile time.

joeltankam commented 5 years ago

Any update please ?

jmecosta commented 4 years ago
<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>

and

      <Private>false</Private>
      <ExcludeAssets>all</ExcludeAssets>

seems to work to me

mpawelski commented 4 years ago

You could use <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> to turn this off for Project to project references.

@thank you @livarcocc It works perfect. Do you know if there is any documentation for this settings? I cannot find anything. I also don't know when it was introduced (in which version of MSBuild/SDK/VisualStudio?)

Not sure if you are the right person to ask, but just to clarify I have some questions:

So this DisableTransitiveProjectReferences setting changes ProjectReference behavior in new SDK-csproj to be the same as in old csproj where ProjectReference wasn't transitive but the transitive projects' .dlls were copied to bin folder ?

Is the effect the same as setting PrivateAssets="All" on all the dependencies of dependand project? Just that with DisableTransitiveProjectReferences you control this behavior in different place, on project that reference other projects, not on project that might be referenced by other projects.

In other word PrivateAssets="All" is more flexible and fine-grained and you can achieve the same end result like with DisableTransitiveProjectReferences but DisableTransitiveProjectReferences is just more useful if you prefer "old non-SDK csproj" non-transitive behavior of ProjectReferences (or if you are in process of migrating to .NET Core or SDK-style csproj)

davidhunter22 commented 3 years ago

I had put a community item in about the bahaviour of mixed C++/CLI, C# solutions that seems related to this https://developercommunity.visualstudio.com/t/Transitive-usage-of-nuget-package-types/1424201 I was wondering if someone knows what the behaviour should be for .vcxproj projects. If it is meant to be non-transitive is there a flag to make it transitive as that is what I needed.

Tobias-Werner-Recom commented 1 month ago

Any update on this question? Is this intended but undocumented?

However, these metadatas are originally applicable only for PackageReference and I can't find any documentation of this behavior for ProjectReference (in the ProjectReference item reference for example). So, should this (PrivateAssets or ExcludeAsssets) be really used in ProjectReference tag ?