NuGet/Home

Feature : Allow project reference DLLs to be added to the parent nupkg for pack target like IncludeReferencedProjects in nuget.exe

joelverhagen opened this issue Β· 360 comments

Steps

  1. dotnet new --type lib two .csproj class libraries: projectA and projectB.
  2. Change <TargetFramework> to <TargetFrameworks> if you don't have #3865.
  3. Make projectA have a ProjectReference to projectB.
  4. Add <Type>project</Type> to the <ProjectReference>.
  5. dotnet pack projectA
  6. Open the resulting .nupkg's lib folder

Expected

projectB.dll should be in the .nupkg along with projectA.dll

Actual

projectB is still a package reference, not a DLL included in the package.

Environment

Tried latest dev's pack target.

dotnet --info

.NET Command Line Tools (1.0.0-preview3-004056)

Product Information:
 Version:            1.0.0-preview3-004056
 Commit SHA-1 hash:  ccc4968bc3

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x64

UPDATE: Please see workaround posted as comment to see how to add ProjectReferences as DLLs in the package dynamically.

Please check spec on wiki for planned behavior

Spec: https://github.com/NuGet/Home/wiki/Adding-nuget-pack-as-a-msbuild-target

This doesn't work:

<TreatAsPackageReference>false</TreatAsPackageReference>

The output .nuspec still has projectB as a package reference and only one file: projectA.dll under lib.

This is a non-trivial feature that hasn't been implemented for RC yet.

Supporting this will require potentially walking the entire project closure to determine all projects that need to be merged into the package, or reading the assets file and matching the project closure with both the project reference flags and the pack flags found in the project (since it can be set either way).

This is also impacted by build outputs not flowing transitively to parent projects yet.

Plan of action:

  1. Build out some automated tests for pack task to cover basic scenarios and detect regression.
  2. Add the original PackageSpec to the lock/assets file.
  3. Add any missing properties to the assets file pack needs (e.g. path to child project output assemblies).
  4. Update restore (no, not pack) to collapse project references when <TreatAsPackageReference> is true.
  5. Move the PackTask away from looking at child projects. Look at restore's assets file instead.

This has repercussions on:

  • Restore: basically all of the data pack needs should be collected by restore an put in the assets file. Restore should be the only guy doing a walk.
  • Project/Package duality: what if a child project .csproj has a <Reference>. How is this collapsed?
  • What collapsing occurs? Certainly build artifacts and <PackageReference>... what about <Reference>?

@rohit21agrawal, I partially got through consuming the assets file from in the pack task:
https://github.com/joelverhagen/NuGet.Client/tree/jver/3891

This also has some progress on getting the output DLLs of child projects (using @(ReferenceCopyLocalPaths) MSBuild items).

This branch is pretty rough so let me know if I can clarify.

As per @rrelyea : #3893 (comment)

"We don't plan to enable this in dotnet pack / msbuild /t:pack in 4.0 timeframe.
We'll listen to customer feedback and consider in the future."

moving to future as this is post-rtm work.

building a nupkg from multiple projects seems like a major feature to be missing :/

@gulbanana thanks for the feedback. this is something that is not planned for the 4.0 RTM release, but this is something we will definitely address in a future release.

@kzu 's nugetizer 3000 does this?

Hi guys,

Just out of curiosity - are there any plans to proceed on this one? Currently, I need to use the dirty workaround that is MSBuild internals specifics:

<ItemGroup>
  <_PackageFiles Include="$(OutputPath)\ReferencedProjectDll.dll">
    <BuildAction>None</BuildAction>
    <PackagePath>lib\net45\</PackagePath>
  </_PackageFiles>
</ItemGroup>
kzu commented

Indeed @zvirja you could try install-package NuGet.Build.Packaging, which should be compatible with "sdk pack" and give you that behavior automatically.

@kzu Thanks for the advice. I tried it for a couple of times, but cannot make it work. After I install this package to .NET Core project, it seems that SDK "Pack" is invoked rather than one from the package. As result it fails because you assign the NuspecFile property to non-existing file.

Package version I tried - 0.1.324.

Is there something that I'm missing? Is that suppose to work with .NET Core SDK projects?

Thanks!

P.S. BTW, was unable to find a bug tracker to ask this question there - is it present somewhere?

kzu commented

@zvirja you need to be on VS 15.3 at least

@kzu Do you mean that MSBuild that is shipped with VS 2017.2 isn't compatible? Currently I run build from the command line, so it's unclear how VS version is involved..

Sent from my Samsung SM-T719 using FastHub

+1 on also having a project with a reference project built in the same solution to include in a single NuGet package.

kzu commented

@zvirja in pre-15.3, the only way to override the built-in SDK 'Pack' target is to build with an explicitly garbage argument like https://github.com/xamarin/Xamarin.VSSDK/blob/master/build.proj#L8

Under MSBuild 15.3 (which == VS 15.3, since one doesn't ship without the other anymore), installing the nugetizer nuget package itself will perform the overriding as necessary.

+1. Can't stress enough how much this is needed for my project.

+1

xenry commented

+1

+1 @zvirja do you have the workaround somewhere I can have a look at?

@UncleFirefox ATM no, as I no longer need this feature. However, you can use smth like:

<ItemGroup>
  <Content Include="$(OutputPath)\ReferencedLib.dll">
    <Pack>true</Pack>
    <PackagePath>lib\$(TargetFramework)</PackagePath>
  </Content>
</ItemGroup>

If you target multiple frameworks, I'd look to smth like below (stolen from here):

<PropertyGroup>
  <TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);IncludeReferencedProjectInPackage</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="IncludeReferencedProjectInPackage" Condition="'$(IncludeBuildOutput)' != 'false'">
  <ItemGroup>
    <TfmSpecificPackageFile Include="$(OutputPath)\ReferencedLib.dll" PackagePath="lib/$(TargetFramework)" />
  </ItemGroup>
</Target>

Hope that helps πŸ˜‰

Setting the PackagePath can even be omitted when using $(TargetsForTfmSpecificBuildOutput):

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;netstandard1.6</TargetFrameworks>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeP2POutput</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\testprivatelib\testprivatelib.csproj" PrivateAssets="All" />
  </ItemGroup>
  <Target Name="IncludeP2POutput">
    <ItemGroup>
      <BuildOutputInPackage Include="$(OutputPath)\testprivatelib.dll" />
    </ItemGroup>
  </Target>
</Project>

@dasMulli 's approach is the recommended one when it comes to adding files that need to go into lib folder.

Also wishing there was a way for project references to be auto-included in nuget packages...

jnm2 commented

I'm having the exact same issue with <PackageReference>. I need PrivateAssets="all" but I also need the referenced DLLs to end up packaged along with the build output. I guess the same approach is the best approach for this?

jnm2 commented

What would be really cool though is to not have PrivateAssets="all" because that way the test project doesn't need to duplicate the package references, but still suppress the nuspec dependency and include it as \lib build output.

Just mentioning this feature is much wanted.

kzu commented
jnm2 commented

Ran into another conversion where I could really use the capability to include the build output from a NuGet package rather than depending on it via nuspec.

jnm2 commented

Looks like the TargetsForTfmSpecificBuildOutput workaround above no longer works in newer SDKs.

Here's the current workaround for including copy-local references from NuGet packages:

  <!-- https://github.com/NuGet/Home/issues/3891 -->
  <Target Name="SomeUniqueName" BeforeTargets="_GetBuildOutputFilesWithTfm">
    <ItemGroup>
      <BuildOutputInPackage
        Include="@(ReferenceCopyLocalPaths)"
        Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' == 'SomeNuGetPackageId'" />
    </ItemGroup>
  </Target>

Would be good to get some of these arms races officially supported. 😜

@jnm2 what were you trying to pack there? "self-contained" tools, msbuild tasks etc? Posted a sample of how to include a publish output in an arbitrary location in a package at dotnet/sdk#1846 (comment)

btw, which version did the TargetsForTfmSpecificBuildOutput fail with? using an underscore-prefixed target is a bad practice (but then, that's why it's a workaround πŸ™ƒ)

Thanks @kzu, Nugetizer solved my problem. It took some trial and error though.

I would think my scenario is quite common. I have an existing solution, quite big, and start to convert the projects with the least dependencies to the new csproj format. This breaks the existing nuget publishing.

jnm2 commented

@dasMulli NUnit pulls NUnit.System.Linq as a NuGet package but prefers to bundle lib\net20\NUnit.System.Linq.dll rather than leaving it as a dependency. I forget offhand what the other times were that I needed this. I think Roslyn analyzers.

I tried to find TargetsForTfmSpecificBuildOutput using the MSBuild structured log viewer with /t:Pack and didn't find it. Maybe I made a mistake? I'll try again if that is preferable to the underscore version.

Any update on this yet?

Adding my voice to this. We're running in to the same issue. Just feels dirty and wrong without it auto-including project references.

if the project reference are more than 2 levels , it is really mess that need to know what dll need to be included and add it to each level .csproj manually . really hope it have a solution soon.

If you do dotnet publish on a project, it will include all the reference dlls in the publish folder. You can then package these into a nuget package manually somehow.

But it's not ideal.

Also adding my +1. This is blocking a fairly simple project for me. I just need a .nuget package containing everything required to run the project.

gynet commented

Nuget team, this is a must-have, please

One workaround that would work with dotnet.exe pack or msbuild /t:pack if you are packing a project with project references and want to include references as DLLs instead of dependencies in output nuspec is shown by the below example:

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net47</TargetFrameworks>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" PrivateAssets="all" />
    <ProjectReference Include="..\ClassLibrary3\ClassLibrary3.csproj" Condition="'$(TargetFramework)' == 'net47'" PrivateAssets="all" />
  </ItemGroup>

  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveProjectReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(_ResolvedProjectReferencePaths)" />
    </ItemGroup>
  </Target>
</Project>

Documentation for these extension points in the pack target can be found here: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#advanced-extension-points-to-create-customized-package

<BuildOutputInPackage Include="@(_ResolvedProjectReferencePaths)" />

From my understanding it is considered "bad form" to take a dependency on a property/item/target that starts with an underscore _. Everything in MSBuild is "public" and "global", and the convention of naming things starting with _ is to make consumers aware that this property/item/target can change in the future, and shouldn't be depended on.

So while this workaround may work now, I don't see that as a long term solution to this problem. And for people to aware when using this workaround that it might not work long term.

Updated workaround:

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net47</TargetFrameworks>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" PrivateAssets="all" />
    <ProjectReference Include="..\ClassLibrary3\ClassLibrary3.csproj" Condition="'$(TargetFramework)' == 'net47'" PrivateAssets="all" />
  </ItemGroup>

  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))" />
    </ItemGroup>
  </Target>
</Project>

Documentation for these extension points in the pack target can be found here: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#advanced-extension-points-to-create-customized-package

CC: @eerhardt

Is there a good way for the NuGet package to import the dependencies that the Project had?
Say Project A depends on Project B, but Project B depends on NuGet Package 1 and Nuget Package 2.
How can I make Project A list the dependences of Project B as its own dependencies?
This workaround is great at getting the DLL into the NuGet package, but I assume things will fail if they depend on something that doesn't exist?

@Tadimsky, @kzu's https://github.com/NuGet/NuGet.Build.Packaging nugetizer project is probably what you are looking for.

@Tadimsky right now there is no good way to do it, but that will be kept in mind when this feature is implemented. For now, your only option is to list it as a dependency in your top level project/

This seems like a very-much needed feature. Why would someone want a separate NuGet for each project dependency?

niwrA commented

Surprised (and disappointed) that this is sitll open ... can we do anything to help?

Open since 2016 ... how is this not yet fixed and released? Please hurry up folks ...

The workaround is fine and I can find projB in the lib folder of the nuget package. However, in the generated nuspec, projB.dll appears as a dependency, so I would need to create and publish a separate nupkg for projB ? Can this be avoided ? (PrivateAssets="All" does not seem to work)

@bgavrilMS did you make sure you restored after adding PrivateAssets=all ? the info is read from assets file, so if the assets file was not updated, it would seem like it did not work.

@rohit21agrawal - thanks for that, I did not realize the asset file was recreated on restore. Works great.

Wow. it's still here. June 2018 πŸ€“

jr01 commented

A small improvement to @rohit21agrawal workaround that allows for mixing 'nuget' projectreferences and 'privateassets' projectreferences .

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net47</TargetFrameworks>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj"  />
    <ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" PrivateAssets="all" />
    <ProjectReference Include="..\ClassLibrary3\ClassLibrary3.csproj" Condition="'$(TargetFramework)' == 'net47'" PrivateAssets="all" />
  </ItemGroup>

  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))" />
    </ItemGroup>
  </Target>
</Project>

ClassLibrary1 is packaged as a nuget and added to the generated nuspec. Classlibrary2/3 (the privateassets) are copied to the lib.

We use the suggested workaround and these two commands to build & package our libraries:

dotnet build -c Release /p:Version=${VERSION_NUMBER} ./src/Service.csproj
dotnet pack -c Release --no-build /p:PackageVersion=${VERSION_NUMBER} ./src/Service -o ./dist

Service.csproj has a project dependency on a project Shared.csproj which should be included in the nuget package. The problem is with DLL versioning - the build step correctly builds both DLLs with supplied version, but the pack step will build the Shared project again with version 1.0.0.0, which then gets into the package. But since the Service DLL was built with a dependency on a different version of Shared, it leads to runtime exception on the package's consumer side.

Obviously the problem is that the workaround doesn't respect the --no-build option. We could modify the pack command to include /p:Version=${VERSION_NUMBER} (just like in the build command), but that just feels wrong, as it will only treat the symptoms, not the problem itself. Is there any way to fix this?

Facing the same problem with my analyzer. It's 2 assemblies/projects and both need to be included in the analyzer's package.

@rohit21agrawal, @eerhardt: Thanks for a nice workaround, but there is still an issue with it. A lot of things do not happen if you merely inject ResolveReferences; in particular, satellite and serialization assembles, XML doc files and PDB symbols would not be resolved, because of the way ResolveAssemblyReference is invoked in Microsoft.Common.CurrentVersion.targets:

    <ResolveAssemblyReference
  . . .
        FindSatellites="$(BuildingProject)"
        FindSerializationAssemblies="$(BuildingProject)"
        FindRelatedFiles="$(BuildingProject)"
  . . .

So a more correct way to collect packable files is to make the injected target dependent on BuildOnlySettings;ResolveReferences. I am using the stanza

  <PropertyGroup>
    <TargetsForTfmSpecificBuildOutput>
      $(TargetsForTfmSpecificBuildOutput);PackReferencedProjectOutputs
    </TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>
  <Target Name="PackReferencedProjectOutputs" DependsOnTargets="BuildOnlySettings;ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
    </ItemGroup>
  </Target>

which adds also doc XML and PDB files into the package.

the BuildingProject property (set to true by the BuildOnlySettings target) should be set to false only during non-build invocations, such as project loading in IDE; setting it to true here is consistent with the rest of the build process (Pack is certainly a build invocation in this sense). It probably would not be incorrect, speaking of the final fix to this issue, to add a dependency on the BuildOnlySettings target even earlier in the process, possibly to the GenerateNuspec target. Otherwise there will be multiple dependency paths leading to ResolveReferences during packing, some setting the flags, others not setting it, and that may cause surprising consequences.

niwrA commented

Some of the above comments make this seem like a difficult problem to solve, but it has been solved indirectly in various other contexts (for instance it works just fine for .NET Framework).

As a development shortcut, to get this feature to Core faster, couldn't the implementation for this be shortcut to calculating what dlls publish would output, then determining which of these are from referenced projects?

Fresa commented

The workaround does not seem to package symbols and source code of the referenced project into the nuget symbol package (only the dll).

@jr01's workaround worked great for us. DLL's from project dependencies end up in the nupkg file, and the package can successfully be installed in another solution. Haven't tested the consuming solution yet though. Thanks for this!

Edit: Now tested a (very simple) consuming solution, no issues.

lokki commented

Can it be made that when a referenced project has IsPackable=false that it is included as DLL and all it's references are included as package references to the packable project?

Any chance of a real solution here than a myriad of workarounds? I don't think it's right that I have to flood nuget with packages just because dotnet pack can't build the dependency tree?

When is this going to get fixed?? This is such a mess

I would love to have been in the room where they decided to make this a much more streamlined process. It is so much easier than old Framework package maintenance.... but then right at the end, the design topic of including other project references into dotnet pack comes up... and it looks like they figuratively drove the whole god damn bus off a cliff.

Two years, multiple issues, over a hundred comments, still not prioritized. Wow guys. Impressive.

This also doesn't work with dotnet build and <GeneratePackageOnBuild>true</GeneratePackageOnBuild>, by the way.

Honestly, we are considering ditchting .Net Core and nuget over this.
We are building microservices which share some commen infrastructure and deploying them using nuget is difficult due to this issue.
Even python packages work better than this.
Hell, I would be satisfied if I could write this by hand, but the whole mess (including sourcelink now) is so complicated that the amount of time needed is prohibitive.

Just spent half a day thinking I was doing something wrong with my project setup, only to find out this behavior is by design. Really disappointed

We still didn't make this happen? It's not a good practise to make each dll as a package - we definitely need to hide some internal projects.

We where facing the same problem here and so I put together a build-script which a lot of stuff I collected from all of the workarounds for this or related problems. Combined with some of our own juice I created a package which if included creates packages similar to the "old -IncludeReferencedProjects" flag of NuGet.exe. It works fine in our internal project-/build-environment and I would like to share it, as it maybe is useful to other too: https://www.nuget.org/packages/OmicronLab.Tools.NugetTargets
I'm sure it does not work in all cases, especially as our build-system has some strange configurations, but I guess it should work in most default cases.
Feel free to extract the targets-file from the package and send me feedback/improvements. If there is enough interest I can also move it from our internal source-control to a public repository, but I hope this script is only needed for a short amount of time until there is an "official way"...

I'm highly disappointed that this is by design.

It's "by design", it's just a bad design :P

Who is the product owner for NuGet at Microsoft? Seems we’re all just customers complaining about a bad design but this needs review from the PM/owner

Wait... this is intentional? πŸ˜‚

Btw i've put together a sample recently with the latest "workaround" at https://github.com/dasMulli/AdvancedMSBuildDemos/tree/master/IncludeP2P (esp. PublicLib.csproj).

Interestingly, I've looked at the code of some folks who needed to use that and found that in many cases, the design is questionable at best.
Needing to add other projects into the package was either required because of unnecessary splitting of assemblies ("shared" code that no one else used or no layering that required it) or because some wanted to share a subset of their projects, but didn't want to manage many packages. Last one nearly resulted in problems where two packages had the same dll files packaged into them which would lead to problematic behaviour if both packages were added to a consuming project (esp. if in different versions).
I really only have seen a handful of legit use cases, but I don't claim to have seen enough to make an informed judgement. I just think that the option of being there led to a lot of "well let's add this switch and we'll be fine" without weighing in or knowing about potential drawbacks.

The design decision to drop support for using NuGet packages to encapsulate multiple assemblies is dissapointing.

This really is disappointing

@houseofcat You just don't understand the point. It's the way of coping with dll-hell: they just decided not to include dll's at all. =)

I had a faint hope .NET will be really cross platform without becoming a JVM. Alas.

Holly mother... With over 1200+ issues with nuget this will make another birthday...

This is very dissapointing. Decisions like this are obviously made by people who are only focused on their work, but can't understand the real world out there.

We need to expose a few Nuget packages within our organization. We DONT want to expose and manage hundreds of them, just because we decided to split them in different internal projects. Period.

Honestly, the tooling isn't even consistent.

How can it try to include a project dependency as a package dependency when it's marked as IsPackable=false? Doesn't make any sense. Obviously it should then include the dll and their package references recursively rather than including it as a package reference.

Thanks to @dasMulli, the real MVP, for a workaround that I can use while microsoft ponders fixing this over another decade or two. (Yes, I'm salty. Spending way to much time on builds and related tooling. I want to get back to my source code...)

I cannot help but ask the commenters to please be more empathic to their fellow engineers working on quite a complex and extensive piece of software. We all, in the end, belong to the same guild.

On a positive note, compare what we already have with the "classic" mode of packaging. Write and maintain a nuspec file. Invoke nuget command in a post-build step. Fail because nuget.exe is not on the path. Or succeed, but needlessly create a package on every build. All in all, we have much better tooling now with the integrated dotnet pack and packaging targets than was even imaginable just a couple years ago. The improvement to NuGet support in the SDK is humongous, and the implementation is so extensive that it's unrealistic to expect not to hit any snags on the way.

Yes, there are bugs and design flaws and omissions, and I, like everyone else, waiting for them to be fixed. The demand for this feature is indeed very high. But workarounds fortunately exist, although maybe not the workaround. "Get back to my source code and forget about all the tooling magic, because it works perfectly every time for everyone" is The Right Thing, naturally. And, being The Right Thing, it is probably unattainable in the real world. But it's very hard to disagree that we are now closer to it than ever before. Objectively, the NuGet team deserve a lot of praise, not (or, at the very least, in addition to) the continuous stream of blame! :)

This is of course not to say that this issue is unimportant. It really is, judging by the unending expression of users' frustration, and I hope it can be prioritized. But, I am repeating myself, it's not strictly a show-stopper.

/cc @emgarten, @rohit21agrawal, you guys are doing a great job. Highly appreciated.

jnm2 commented

@kkm000 Thank you for saying so! I agree.

As a workaround, I instead used Fody/Costura to merge the specific assemblies into the nuget dll package.

I had to develop a postprocessing tool that merges nupkg files. Config looks like this:

{
  "Packages":
  [
    {
      "Id": "Avalonia",
      "MergeAll": true,
      "Exclude": ["Avalonia.Remote.Protocol"],
      "Merge": [
        {
          "Id": "Avalonia.Build.Tasks",
          "IgnoreMissingFrameworkBinaries": true,
          "DoNotMergeDependencies": true
        },
        {
          "Id": "Avalonia.DesktopRuntime",
          "IgnoreMissingFrameworkBinaries": true,
          "IgnoreMissingFrameworkDependencies": true
        }
      ]
    }
  ]
}

Not ideal, but it works for us.

Hello there..

I have ended with a mix of solutions from this issue. We try to have conventions and in the end we have stayed with the next one.... Basically copy the referenced project asssemblies only if PrivateAssets="All".

eg:

  • Directory.build... or Shared.msbuild for all the projects.
<Project>

  <PropertyGroup>
    <PackageId>$(AssemblyName)</PackageId>
    <Description>$(AssemblyName)</Description>
	...
  </PropertyGroup>

  <PropertyGroup>
 <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>
  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
    <ItemGroup>
		<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths-&gt;WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')-&gt;WithMetadataValue('PrivateAssets', 'All'))" />
    </ItemGroup>
  </Target>
  • ProjectA.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>net461;netstandard2.0</TargetFrameworks>    
    <PlatformTarget>AnyCpu</PlatformTarget>    
  </PropertyGroup>
  
  <Import Project="$(MSBuildThisFileDirectory)..\Shared.msbuild" />

  <ItemGroup> 
    <ProjectReference Include="..\ProjectB\ProjectB.csproj" PrivateAssets="None" /> (As Package)
    <ProjectReference Include="..\ProjectC\ProjectC.csproj" PrivateAssets="All" /> (Copy assemblies)
	<ProjectReference Include="..\ProjectD\ProjectD.csproj"  /> (As Package)
</ItemGroup>

  <ItemGroup>
    <Compile Include="..\SharedAssemblyInfo.cs">
      <Link>Properties\SharedAssemblyInfo.cs</Link>
    </Compile>
  </ItemGroup>

</Project>
  • CI
dotnet pack .\src\ProjectA\ProjectA.csproj --configuration Release --no-build --no-restore --output ..\artifacts -p:PackageVersion=1.2.3

Thank you all!

Small tidbit of detail. ProjectA has a Project-dependency to ProjectB. In ProjectA's .csproj file, I added this line manually:

 <ItemGroup>
 <_PackageFiles Include="$(OutputPath)\ProjectB.dll">
      <BuildAction>None</BuildAction>
      <PackagePath>lib\$(TargetFramework)</PackagePath>
    </_PackageFiles>
  </ItemGroup>

I noticed the generated .nuspec file in the obj/release folder did NOT contain a second file, ProjectB:

<files>
    <file src="ProjectA\bin\Release\netcoreapp2.2\ProjectA.dll" target="lib\netcoreapp2.2\ProjectA.dll" />
  </files>

Only after adding PrivateAssets=all to ProjectA's csproj file for ProjectB did it then show up in the nuspec:

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

Without doing the above, my ProjectC (which needed ProjectA+B nuget package) would complain of a missing reference to ProjectB.

This option works for now, thanks everyone for the tips. This really saves the need to have to create multiple nuget packages for each project/dll.

@rohit21agrawal, @eerhardt

This workaround works perfect! But is there any possibility to add .PDB files also?

@bail16 May take a look at the NuGet-Package I linked above. The script in there should include everything you need - just take the part you want :-)

@chrfin This is awesome, i copied this lines of code:

https://gist.github.com/bail16/b0815e9d959aa960a8e4c7e015b70270

but only referenced pdb files are copied, how to insert main project pdb file, without using IncludeSymbols which creates another package... (I'm working on multi target frameworks)

ran into this issue with dotnet pack and after hours of googling finally got here.. and its now more than a year and this shit is still not fix.. wtf?

@satuday I feel your frustration. Yesterday I waded through all the responses above. For my project, one of the suggestions above worked fine. I copied the following into my *.csproj file and dotnet pack worked like a charm. it didn't even feel like a workaround.

  <!--
    The following solves the problem that 'dotnet pack' does not include the DLLs from referenced projects.
    See https://github.com/NuGet/Home/issues/3891 for a description of the problem
    and for newer versions / workarounds / built-in methods.
  -->
  <PropertyGroup>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
    <!-- include PDBs in the NuGet package -->
    <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
  </PropertyGroup>
  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
    </ItemGroup>
  </Target>

...and add PrivateAssets="all" to each of your <ProjectReference ...>.

@WallaceKelly thanks. that works. I also added PrivateAssets="All" to the ProjectReference so that it would not show as a dependency in for the nuget package

niwrA commented

I have been thinking about this a lot, but The argument about messing up dll-s in various packages and create conflicts seems a valid one. If you truly need your code to be in separate dlls then I think they are also better off being separate nuget packages that you link to as nuget packages from the other projects.

Whereas if you just want to have your code in separate projects but packaged together, I would use shared projects instead. These contain just your code and you then reference these to a single class library project that you package. As a bonus, this makes it very easy to support Core and NET packages efficiently too and different versions - just make one class library project for each platform you want to support and link all the shared projects to them.

Sorry but simply no. Packaging framework shall not dictate project or dependency tree structure. At least a normal one. And current situation is not normal.

niwrA commented

Easily said, but if you understood what Shared Projects are and how they work, you would probably wonder why this isn’t the default way of working already. Project structure does not change. Dependency structure is greatly cleaned up. And it is as simple as moving your classes to the new Shared Projext and linking to that shared project from the dll.

(And I have in fact run into problems with combining projects as dlls into one nuget package. )

kzu commented

Shared projects are thankfully dead. Long live SDK-style multi-targeting projects.

@niwrA - Shared Projects unfortunately run into conflicts when you have a project that consumes two different libraries that both depend on a base "shared" library.

For example:

  • Contracts - A basic library containing all of my shared classes for requests
  • Client - A client library that depends on contracts. This is a NuGet package that is shared externally.
  • Service Utilities - Some helper code that depends on Contracts to do something like Validation on the Contracts objects that is only used internally.

Now, if I create a Service, and I pull in both the Utilities and Client libraries, if they depend on Contracts as a Shared Project, then I'm now pulling in two different definitions of the same classes.

Creating separate nuget packages for Contracts and Client does solve the problem, but in this scenario I never want Contracts and Client to use different versions. I only have a single NuGet package which contains Client and Contracts. My service which is internal consumes the Utilities, Client, and Contracts assemblies directly without using a NuGet package.

Nobody here is saying that there are no issues with combining multiple project DLLs into a single packge, but there are valid reasons for wanting to support it as well.