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

PackageReference: Need control over the selection for individual .dlls #5986

Open
KirillOsenkov opened this issue Oct 4, 2017 · 17 comments
Open

Comments

@KirillOsenkov
Copy link

Details about Problem

NuGet product used (NuGet.exe | VS UI | Package Manager Console | dotnet.exe):
MSBuild 15.3 elements.

VS version (if appropriate):
15.3 or newer

Detailed repro steps so we can see the same problem

I don't see a way to control whether the .dll from PackageReference gets copied to output or not.

Additionally, if I have ProjectA with a PackageReference, and ProjectB referencing ProjectA, I want to make sure that when ProjectB builds, the .dll inherited transitively from ProjectA doesn't get copied to output (but I want it to get copied for ProjectA, and they have different output directories).

There needs to be a way to control this.

@PatoBeltran
Copy link

@nkolev92 can you please take a look at this?

@nkolev92
Copy link
Member

nkolev92 commented Oct 5, 2017

@KirillOsenkov
If I understand your scenario correctly
<PrivateAssets>all</PrivateAssets> should do it.

https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets

@KirillOsenkov
Copy link
Author

Not quite.

In non-SDK-style projects, set the <CopyNuGetImplementations>false</CopyNuGetImplementations> to not copy the binaries from the referenced packages to output. This also applies to transitively "inherited" packages from PackageReference elements in referenced projects.

In SDK-style projects there's a similar property: <CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>

I'm also looking for a way to copy some .dlls to output, but not others. For instance, https://www.nuget.org/packages/Microsoft.VisualStudio.Composition/15.3.38 - I want to copy Microsoft.VisualStudio.Composition.dll to output, but I don't want any of the satellite resource assemblies. I wasn't able to find a way to do that.

@KirillOsenkov
Copy link
Author

Also with PrivateAssets the problem is that I do want a reference to the .dll added to my project, but not copied to output. With CopyNuGetImplementations=false I can just rely on the inherited transitive package references. However to use PrivateAssets I need to duplicate the PackageReferences from ProjectA in ProjectB, which defeats the purpose of transitivity.

@rohit21agrawal
Copy link
Contributor

ExcludeAssets=runtime does that, but it will apply to every dll in the lib folder

@nkolev92
Copy link
Member

nkolev92 commented Oct 6, 2017

Clear.
Currently we don't have any type of granular control of package assets of the same category.
//cc
@emgarten

@rrelyea
Copy link
Contributor

rrelyea commented Oct 10, 2017

Does @rohit21agrawal's proposal help? (excludeassets runtime)

@rrelyea rrelyea added Type:DCR Design Change Request Triage:Investigate labels Oct 10, 2017
@KirillOsenkov
Copy link
Author

Nope, not quite, see my description above.

@mungojam
Copy link

Interesting, I think I'm after the opposite (roslyn 22095) where transitive .net DLLs are copied to output, but not included as references (or at least their APIs are not exposed).

@nkolev92 nkolev92 added this to the Backlog milestone Oct 17, 2017
@nkolev92 nkolev92 added Priority:2 Issues for the current backlog. Triage:NeedsTriageDiscussion and removed Priority:2 Issues for the current backlog. labels Oct 17, 2017
@nkolev92 nkolev92 changed the title PackageReference: Need control over CopyToOutput for individual .dlls PackageReference: Need control over the selection for individual .dlls Feb 20, 2020
@nkolev92 nkolev92 removed the Type:DCR Design Change Request label Aug 25, 2020
@kirsan31
Copy link

kirsan31 commented Nov 23, 2020

Very needed, to deal with not correctly configurated nugget packages. For example, for now Microsoft.Web.WebView2 copies all dll to output in any case:

Microsoft.Web.WebView2.Core.dll
Microsoft.Web.WebView2.WinForms.dll
Microsoft.Web.WebView2.Wpf.dll

But in WinForms we absolutely no need of Microsoft.Web.WebView2.Wpf.dll...
Wood be useful if ExcludeAssets can handle names for input, for example: ExcludeAssets="Native;Microsoft.Web.WebView2.Wpf.dll;Microsoft.Web.WebView2.WinForms.dll"

@KirillOsenkov
Copy link
Author

KirillOsenkov commented Nov 23, 2020

I've found that you can manually control which .dlls to reference and which .dlls to copy to output from a NuGet package if you turn off all assets on the PackageReference:

<PackageReference Include="Foo.MyPackage" Version="1.0.0" ExcludeAssets="all" GeneratePathProperty="true" />

and then manually reference the .dlls you need:

<Reference Include="$(PkgFoo_MyPackage)\lib\net45\CopyThis.dll">
  <Private>true</Private>
</Reference>
<Reference Include="$(PkgFoo_MyPackage)\lib\net45\DontCopyThis.dll">
  <Private>false</Private>
</Reference>
<None Include="$(PkgFoo_MyPackage)\lib\net45\CopyThisButDontReference.dll">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

Note the usage of GeneratePathProperty on the PackageReference to generate the Pkg*** property, where the package name is prefixed with Pkg and dots replaced with underscores.

@kirsan31
Copy link

kirsan31 commented Nov 24, 2020

@KirillOsenkov very cool workaround - thank you! I see here only 3 drawback:

  1. You need to know exactly the version of the framework from the package. E.g. \lib\ netcoreapp3.0 \CopyThis.dll. And if it will changed - all stop work :(
  2. xml docs will be copied to Release dir too.
  3. I think individual exclude will be more helpful than individual include overall.

@yyjdelete
Copy link

This make it difficult to build an actual plugins with more than one level (transitive) reference instead of just sample like https://github.com/dotnet/samples/tree/main/core/extensions/AppWithPlugin, without manually or write an tools to remove duplicate assembly files and maybe also rewrite .deps.json to remove them.

For example, Plugin.Host(ProjectReference) can be excluded all times
But only Microsoft.EntityFrameworkCore.Relational itself instead of all its depencies was excluded from Plugin.Shared1, and you get 11 duplicate files, and you require to find all 12 PackageReference nodes(it may changes between versions) and add them manually for just exclude a special version of PackageReference.
And what's worse, even Microsoft.EntityFrameworkCore.Relational will not be removed from Plugin.Plugin1, so it mean you should do this again and again for all projects in lower level of the reference tree.


Plugin.Host

<Project Sdk="Microsoft.NET.Sdk">

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

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.1" PrivateAssets="all" />
  </ItemGroup>

</Project>

Plugin.Shared1

<Project Sdk="Microsoft.NET.Sdk">

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

  <ItemGroup>
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.1" ExcludeAssets="runtime" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Plugin.Host\Plugin.Host.csproj" Private="False">
      <ExcludeAssets>runtime</ExcludeAssets>
    </ProjectReference>
  </ItemGroup>

</Project>

Plugin.Plugin1

<Project Sdk="Microsoft.NET.Sdk">

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

  <ItemGroup>
    <ProjectReference Include="..\Plugin.Host\Plugin.Host.csproj" Private="False">
      <ExcludeAssets>runtime</ExcludeAssets>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.Shared1\Plugin.Shared1.csproj" />
  </ItemGroup>

</Project>

@mattyoungms
Copy link

mattyoungms commented Feb 18, 2022

I had the same issue packaging a single file executable, unnecessary transitive dependencies were bloating the package size. Enabling trimming broke the exe in strange ways, so I came up with this manual hack to remove files from the package prior to packaging.

  <Target Name="CleanupTransientDependencies" AfterTargets="PrepareForBundle">
    <Message Text="CleanupTransientDependencies..." Importance="high" />
    <!--<Message Text=" PRE: @(FilesToBundle)" Importance="high" /> -->
    <ItemGroup>
      <!-- Remove unused transient dependencies from the self-contained exe to reduce it's size. -->
      <FilesToBundle Remove="@(FilesToBundle)" Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(FilesToBundle.Identity)', 'FileName.To.Remove.dll'))" />
      </ItemGroup>
      <!--<Message Text="POST: @(FilesToBundle)" Importance="high" /> -->
  </Target>

@BhaaLseN
Copy link

BhaaLseN commented Mar 6, 2022

I recently ran into a similar issue (a package using an optional transitive dependency that causes my project to fail compilation due to a class/namespace conflict), and found #5986 (comment) as suitable workaround.

However: I had to adjust the code slightly, because the build would should "Cannot find reference 'path/to/referenced.dll'" warnings that didn't affect compilation:

<PackageReference Include="Foo.MyPackage" Version="1.0.0" ExcludeAssets="all" GeneratePathProperty="true" />
<Reference Include="CopyThis">
  <HintPath>$(PkgFoo_MyPackage)\lib\net45\CopyThis.dll</HintPath>
  <Private>true</Private>
</Reference>

I wasn't really able to get this to work in any other way (using ExcludeAssets, IncludeAssets, Aliases etc.) so I'm interrested in a properly supported way of choosing which included assemblies to reference and not reference. It would be nice if this could go both ways though:

  1. My initial problem was GemBox.Spreadsheet which also includes BouncyCastle - and for whatever reason, referencing it causes a namespace Standard to be in scope that conflicts with any project defined class Standard.
    Here, excluding BouncyCastle is the goal; or at least making sure code that doesn't deal with the references need not change. Aliases would've been a workable solution, but instead they didn't work on VS 2022 17.1 on a .NET Framework 4.8 project.
  2. The other candidate would've been Saxon-HE which specifically only referenced the main assembly in the past (until 10.5.0), causing other code that consumes internals to extend Saxon functionality to not work with the NuGet package. The files were there, but I had no way of getting them to be referenced (which would've been possible with GeneratePathProperty, had I known about it).
    Here, including other references such as saxon-he-10.5.0.dll or specific IKVM ones is the goal; in order to write extensions.

For both cases, I obviously also want them to end up in the output directory when they're necessary at runtime (which is a must, because things wont work otherwise); or exclude them if they're not (which is a may; and I don't mind if they're there anyways).

@dotMorten
Copy link

I have a slightly different scenario that requires this.
I maintain package A. Someone else maintains Package B which takes a dependency on Package A v1.0. That person reports a bug with package A and sends me a sample using Package B in the repro sample.

Now I'd like to replace the transitive dependency A with a project reference to A instead. If I add A as a project reference and B as a nuget reference, I get assemblies from both the package transitively and project reference of A. I want to have project B just completely ignore its A dependency as if it wasn't there, so I don't get library, contentfiles and build.props conflicts.

@PathogenDavid
Copy link
Contributor

@dotMorten That scenario sounds more like dotnet/sdk#1151

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

No branches or pull requests