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

Created on 8 Nov 2016  ยท  242Comments  ยท  Source: NuGet/Home

Steps

  1. dotnet new --type lib two .csproj class libraries: projectA and projectB.
  2. Change <TargetFramework> to <TargetFrameworks> if you don't have https://github.com/NuGet/Home/issues/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.

Pack Backlog 2 PackageReference DCR

Most helpful comment

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

All 242 comments

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. [x] Build out some automated tests for pack task to cover basic scenarios and detect regression.
  2. [x] 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 : https://github.com/NuGet/Home/issues/3893#issuecomment-265843267

"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>

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?

@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.

@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

+1

+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...

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?

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.

Nugetizer already does this ;)
On Wed, Jan 10, 2018 at 4:22 PM Paul den Dulk notifications@github.com
wrote:

Just mentioning this feature is much wanted.

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/NuGet/Home/issues/3891#issuecomment-356708485, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAKW6yfO1vqGTkwTiwTdmjNXimwLB2nLks5tJQ4CgaJpZM4Kr815
.

>

--
/kzu from mobile

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.

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 https://github.com/dotnet/sdk/issues/1846#issuecomment-356726385

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.

@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.

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?

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 ๐Ÿค“

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.

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?

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.

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.

@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.

@JDCain Fyi https://github.com/Fody/Fody/blob/master/readme.md#licensingpatron-faq

D'oh. Thanks for the heads up.

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

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.

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. )

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.

@WallaceKelly Thanks for the workaround. The workaround seems to have an issue though - The PDB of the main project is included, but the PDBs of the referenced projects are not included.

@WallaceKelly It worked, however a new problem arise since the package references from the other project isn't applied to the package :|

Well, back to the drawing board...

Oh, I remember a day in 2001 when Microsoft promised that .NET was the end of DLL hell... Right...

@dozer75 I am exactly in the same state. Did the drawing board give you any answers?

Just to be clear, this is the dependency chain:

PrivateProjectA <-- PrivateProjectB <-- PublicNugetPackageX

@WallaceKelly 's workaround works totally fine _if_ you don't have the last PublicNugetPackageX dependency.

In that case, at runtime you have encounter the following error:

System.IO.FileNotFoundException: 'Could not load file or assembly 'PublicNugetPackageXs, Version=X.X.X.X, Culture=neutral, PublicKeyToken=xxxxxxxxx' or one of its dependencies. The system cannot find the file specified.'

(note that if the nuget dependency was directly on the main PrivateProjectA, instead of the secondary PrivateProjectB, the packing works totally fine)


Possible solutions:

One solution would be to add ALL nuget packages from the sub-projects to the main project (in this case: PrivateProjectA). But in terms of maintainability that would be a nightmare.

Another solution would be to have a script that scans all dependencies and automatically adds them to the main project. I might give it a try.

I am using CakeBuild, so I could: scan -> modify the csproj accordingly -> pack -> reset the modifications (so they don't bother me while I debug my code with VisualStudio)

@xavierpena I'm finding the same issue myself, I would be very interested if you come up with a solution

I have built a c# script that does the whole packing process.

Here it is, all in one single .cs file:

https://gist.github.com/xavierpena/6d6999923072e8d6b4ce0d604d88c8cd

It basically scans all the nuget packages in all the referenced sub-projects, and temporarily adds them to the top project during the "dotnet pack" process.

Usage:

var conf = new PackageConfig()
{
    NugetOutputDirectory = @"C:\test\nuget\export",
    ProjDirectoryPath = @"C:\code\MySolution\src\MyProject",
    ProjectFileName = "MyProject.csproj",
    Runtime = "net461",
    PackageVersionNumberStr = "1.0.0"
};

DotnetPackerStandalone.Pack(conf);

And... that's it.

It still needs the user to modify just once the .csproj file, as stated by @WallaceKelly (it is also explained at the top of my code). Other than that, it automatizes the bundling process.

Any update?

what is the accepted workaround here...
I need to add files to the file element in the generated nuspec file.

@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.

Fair enough, I would indeed always have the contracts as a separate package, so would not run into this issue very quickly. If I would want to link up the versions between for instance a contract and a client that implements it, I would probably do that differently, for instance as a build requirement in CI.

@xavierpena Well, I am dropping dotnet pack and using nuget pack instead and will create a nuspec file manually with all configuration in that one. It will require some manual work but my project won't get many assemblies so it's not an issue.

@xavierpena Well, I am dropping dotnet pack and using nuget pack instead and will create a nuspec file manually with all configuration in that one. It will require some manual work but my project won't get many assemblies so it's not an issue.

Same for me... I tried to remove nuspec when switching to new csproj but that's not possible due to missing features so I rollbacked to classic NuGet pack instead of dotnet task

You're telling me nuget.exe pack is 100% capable of gathering up all the P2P references and bundling them into a single nupkg, but you can't get this to work in dotnet pack???

This is a disaster! It's been over 2 years now! Is there any serious dialogue taking place to fix this issue? All I see is a bunch of hacked up work arounds at this point...

This is indeed a pity that dotnet pack could not handle a dependencies management properly as any other modern packaging system.
In the meantime, this simple solution proposed by @leonard-thieu worked for me
https://github.com/dotnet/cli/issues/3959#issuecomment-355648037

All the package references from the subprojects (included) of my packaging project were referenced properly in the nuget spec

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="ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
    </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 any way to extend this solution so that it also copies any PackageReferences from ProjectReferences into the resulting .nuspec file?

I notice that this is on backlog, but also priority 1. That's confusing to me (assuming priority 1 means what I think it means). If it is on backlog, that means that there is no serious work being done on it. I know that it probably won't happen, but is there any way that one of the people that are actually working on the project could comment and indicate whether they are taking this particular problem seriously?

We have mixed feelings about this. Top vote getter. But causes problems for people too.

We will be analyzing and discussing with both sides. But not right away...
Backlog p1 issues are likely going to worked on before backlog p2.

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="ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
    </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 any way to extend this solution so that it also copies any PackageReferences from ProjectReferences into the resulting .nuspec file?

Same issue here, how can i get the referenced assembly and ITS dependencies following. I mean an assembly without its PackageReferences has no point actually .. And i can't imagine a project reference can't expose its PackageReference.

I just wanted to add my voice to this as a vote for getting this resolved, as my impression from the thread above is that this is not being taken seriously, which strikes me as very odd. I don't understand how this hasn't been dealt with sooner.

As things stand, I cannot create a nuget package that depends on private libraries, i.e. libraries that are needed at runtime, but should not be exposed as a nuget package.

I either have to change the way I break down my dependencies (i.e. not breaking the private code into a separate library, even though it should be), or I have to expose all of the libraries as nuget packages, even when it's not appropriate. Neither of these options are good. I shouldn't have to avoid best practices, just to work around the deficiencies of the tooling.

How has the team not encountered this issue? And why are they so apathetic about addressing it? It's a huge showstopper.

It's not a showstopper since there are various workarounds. Once upon a time to create a package you would maintain a nuspec and call nuget.exe to create the package. This has always worked and continues to work. (Many people still are using this method.)

It is.

How is it stopping a show if you can go back to building NuGet packages the original way, using your own nuspec file which lists the transitive dependencies and the bundled files? I contribute to open-source projects that do this for this exact reason.

"How a missing feature in thing B can be show stopper while you always can go back and use thing A" is this what you're saying?

Exactly, yeah. It has me a little confused that this could stop someone's show, at least in the traditional meaning where it's making a higher-level goal (such as obtaining a nupkg file for your project) impossible. It makes it sound like something big is riding on this.

@jnm2 @abatishchev The reason he is frustrated is because of the types of responses you just gave. @matthewjones555 with myself included just want a tool that works well out of the box. We don't have time to manage special cookie nuspec files (nor do I want to have to learn how all of its intricacies work because there is not any well documented here's how to do complex/advanced nuspec techniques. Dotnet core is all about the turnkey approach. Is it not? (Otherwise, why did you bundle nuget into dotnet?)

I hope you understand. It's not that it can't be done. The path of migration from .NET Framework is very unforgiving; however given enough time and money we can do anything, but it's a tough sale to management. This problem is exceptionally prominent in large projects migrating from the .NET Framework world.

So why not make the tool work better? What's the harm in that?

@TannerBragg You make good points, thanks for the perspective. I'll try to avoid being frustrating. I hope Microsoft does make the tool work better; it'll make my life easier too.

I used the nuspec format for many years at my company and in open-source projects. It's well-documented and never struck me as intricate. Maybe some of you could benefit and gain immediate relief by exploring it a bit? Maybe not. This is all I should have said, I see. I apologize.

Otherwise, why did you bundle nuget into dotnet?

Just to be clear, I'm not part of Microsoft. I'm an ordinary developer following this issue for the same reason as you, one who often finds himself a little off the beaten path and who also doesn't particularly enjoy the extra work that goes along with that.

@jnm2

It's well-documented and never struck me as intricate.

You are correct. It is not impossible to do. It is just frustrating when you start trying to find concrete examples in the modern nuspec vs legacy nuspec. They have some key differences. Having custom build targets on hundreds of projects can become a major headache and make maintainability tough.

Just to be clear, I'm not part of Microsoft. I'm an ordinary developer following this issue for the same reason as you, one who often finds himself a little off the beaten path and who also doesn't particularly enjoy the extra work that goes along with that.

I apologize for the accusation. I meant "you" as Microsoft - Not you directly.

Perhaps at the end of the day I'm going about it all wrong? Perfect. Even more reason to a better tool.

Thankyou everyone for the extra input and replies to my post.

After reading your replies, there was one point I'd like to address. To paraphrase it a little:

"If dotnet pack isn't working for you, why don't you go back to using nuget pack?"

This is a good point. Nuget pack works well, but unfortunately it has its own limitations. I'm using a template nuspec file alongside a csproj file, and nuget knows to use the csproj for looking up certain values. Unfortunately, nuget.exe doesn't understand multi targeting, and the issue about it on the github tracker says something along the lines of "nuget support is being built into dotnet, so we're not going to continue supporting this, as there's no point in duplicating effort".

So, do you see the predicament? On one hand, I've got a legacy tool, that I'm being told by the developers to migrate away from, and on the other hand I've got the new tool, where the team refuses to fix a multi year old issue, and the community is telling me to go back to the legacy tool ๐Ÿ˜‚

I'm not asking for this to be fixed tomorrow, it would be enough to know that this will be fixed at some point, but from looking at the other posts in this thread, it seems the developers are reticent, which is a shame.

To add to the problem, the spread of microservices really adds to the number of nuget packages. I.e. imagine that we have a microservice which exposes an API and ALSO publishes a client-package for that api. The most logical solution layout is like a server project, a c client project and a common poco class project. Now getting that to build is an absolute PITA and if you mix in that you do not one microservice but a dozen, you have a proble.

@extremeandy @Rayzbam have you guys tried out my solution yet? It should do what you're looking for.

@leonard-thieu We don't want to implement your solution 100 or 1000 times over. It's not maintainable large scale. Sorry to turn my nose to it, but we want to the tool to do it. Thanks, for the offer.

@leonard-thieu We don't want to implement your solution 100 or 1000 times over. It's not maintainable large scale. Sorry to turn my nose to it, but we want to the tool to do it. Thanks, for the offer.

@leonard-thieu We don't want to implement your solution 100 or 1000 times over. It's not maintainable large scale. Sorry to turn my nose to it, but we want to the tool to do it. Thanks, for the offer.

My message was intended for the two people looking for a workaround.

I understand that your scenario is beyond the scope of the workaround and I do hope a first party solution eventually gets implemented.

I can't believe it's been two and a half years now. SMH

@leonard-thieu how do you get it to include all the dependencies in the symbols.nupkg when you use the --include-symbols or --include-source switches?

We have mixed feelings about this. Top vote getter. But causes problems for people too.

We will be analyzing and discussing with both sides. But not right away...
Backlog p1 issues are likely going to worked on before backlog p2.

What problems would it cause to make it optional as it is in nuget.exe?

@DaveSneddon --include-symbols should just work. See https://github.com/leonard-thieu/IncludeDependents. As for --include-source, I couldn't get it working again. It's been awhile since I've used this and I've forgotten how --include-source works.

In case this hasn't already been said (difficult to go through 170+ comments) -

This behaviour is very tricky to work with when using the dotnet linux sdk docker images.

In this scenario the only way to get nuget.exe seems to be to install mono during your build/on base image, then download the nuget.exe i.e via wget, then invoke "nuget pack".

You can apt-get nuget, but it is a trimmed down version that does not support the "pack" command!

This means that you must resort to creating a nuspec file, and managing all your file dependencies manually, i.e the headache that around 80 of us are here complaining about ๐Ÿ˜Š

@leonard-thieu I've tested your solution it doesn't include the project references of the projects referenced by the source project unlike the other workaround.

The problem with nuget pack is embedded views in razor ui project don't work.

Here's a PowerShell script to build nuget packages for all the projects found in a solution file. This seems to work as you can continue to use project references during development but when you build nuget packages these references become nuget references so this script makes it easier to build all these packages with the same version from a single command.

function Build-NuGetPackages {
    [Cmdletbinding()]
    Param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string[]]$SolutionFiles,

        [Parameter(Mandatory = $true, Position = 1)]
        [string]$Version,

        [Parameter(Mandatory = $true, Position = 2)]
        [string]$OutputPath
    )

    $projects
    Foreach ($slnPath in $SolutionFiles)
    {
        # Find a list of all projects added to solution
        $list = Select-String -path $slnPath 'Project.*"([^"]*.csproj)' -AllMatches | Foreach-Object {$_.Matches} | Foreach-Object {$_.Groups[1].Value}
        # Change project paths from relative to absolute (script can be running from different folder than solution file)
        $list = $list | Foreach-Object { Join-Path (Split-Path -Path $slnPath) -ChildPath $_ -Resolve }
        # Merge with projects found from other solutions
        $projects = $projects + $list | Select-Object -Unique
    }
    # Filter projects to only those that contain nuget metdata
    $projects = $projects | Where-Object { Select-String -Path $_ '<PackageId>' -SimpleMatch -Quiet }

    # Build nuget packages for all projects
    Foreach ($project in $projects)
    {
        Invoke-Expression "dotnet pack -o `"$OutputPath`" -p:PackageVersion=$Version `"$project`""
    }
}

Can someone check I have implemented the work around correctly?

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

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <Description>Loads settings from whsConfiguration and provides access to them via the default IConfiguration and IOptions interfaces.</Description>
    <AssemblyVersion>1.0.6.0</AssemblyVersion>
    <PackageReleaseNotes>Added the ability to be able to update and insert table settings</PackageReleaseNotes>

    <FileVersion>1.0.6.0</FileVersion>    
    <VersionPrefix>1.0.6</VersionPrefix>
        <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>

    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.1" />
    <PackageReference Include="System.Data.SqlClient" Version="4.6.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Whds.Configuration.Models\Whds.Configuration.Models.csproj" PrivateAssets="all"/>
  </ItemGroup>

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

</Project>

When this gets built with dotnet pack, the resulting nuget package has the Whds.Configuration.Models.dll within it, but when i try to use the nuget package I get the following error:

Restoring packages for C:GITWhds.ConfigurationWhds.Configuration.TestApiWhds.Configuration.TestApi.csproj...
GET https://api.nuget.org/v3-flatcontainer/whds.configuration.models/index.json
GET http://redacted/FindPackagesById()?id='Whds.Configuration.Models'&semVerLevel=2.0.0
OK http://redacted/FindPackagesById()?id='Whds.Configuration.Models'&semVerLevel=2.0.0 29ms
NotFound https://api.nuget.org/v3-flatcontainer/whds.configuration.models/index.json 517ms
NU1101: Unable to find package Whds.Configuration.Models. No packages exist with this id in source(s): Microsoft Visual Studio Offline Packages, NEXT main repo, NEXT third party repo, NEXT-Nuget.org, nuget.org
Package restore failed. Rolling back package changes for 'Whds.Configuration.TestApi'.
Time Elapsed: 00:00:01.3421175
========== Finished ==========

Have I missed something? Why is it looking for a nuget package for Whds.Configuration.Models?

Can someone check I have implemented the work around correctly?

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

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <Description>Loads settings from whsConfiguration and provides access to them via the default IConfiguration and IOptions interfaces.</Description>
    <AssemblyVersion>1.0.6.0</AssemblyVersion>
    <PackageReleaseNotes>Added the ability to be able to update and insert table settings</PackageReleaseNotes>

    <FileVersion>1.0.6.0</FileVersion>    
    <VersionPrefix>1.0.6</VersionPrefix>
        <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>

    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.1" />
    <PackageReference Include="System.Data.SqlClient" Version="4.6.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Whds.Configuration.Models\Whds.Configuration.Models.csproj" PrivateAssets="all"/>
  </ItemGroup>

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

</Project>

When this gets built with dotnet pack, the resulting nuget package has the Whds.Configuration.Models.dll within it, but when i try to use the nuget package I get the following error:

Restoring packages for C:GITWhds.ConfigurationWhds.Configuration.TestApiWhds.Configuration.TestApi.csproj...
GET https://api.nuget.org/v3-flatcontainer/whds.configuration.models/index.json
GET http://redacted/FindPackagesById()?id='Whds.Configuration.Models'&semVerLevel=2.0.0
OK http://redacted/FindPackagesById()?id='Whds.Configuration.Models'&semVerLevel=2.0.0 29ms
NotFound https://api.nuget.org/v3-flatcontainer/whds.configuration.models/index.json 517ms
NU1101: Unable to find package Whds.Configuration.Models. No packages exist with this id in source(s): Microsoft Visual Studio Offline Packages, NEXT main repo, NEXT third party repo, NEXT-Nuget.org, nuget.org
Package restore failed. Rolling back package changes for 'Whds.Configuration.TestApi'.
Time Elapsed: 00:00:01.3421175
========== Finished ==========

Have I missed something? Why is it looking for a nuget package for Whds.Configuration.Models?

I think you want your BuildOutputInPackage &lt to be an &gt ;)

Please add this!

The fact that this has not been fixed for almost 3 years is unbelievable

yeah... just hit the wall with this one... What is the best "workaround"?

Tried the upper workaround, if it worked, it does not work anymore.
Talking about:

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

@joslat It is not a simple workaround, but I have been using it successfully for months already and it works flawlessly:

https://github.com/NuGet/Home/issues/3891#issuecomment-472978574

All the other "simple" workarounds failed for my use case, that is: I needed the "depth" of the dependency packaging to be bigger than 1:

Project1 <- Project2 <- ... <- ProjectN (Project1 being the top project, and the others being chained dependencies of this top project)

  • Standard solution: "packs Project1+Project2" (stops at Project2)
  • Desired solution: "packs Project1+Project2+...+ProjectN"

If you just need the former case, it is quite simple. If you need the later, I haven't found any other solution other than the code I've linked at the top.

yeah... just hit the wall with this one... What is the best "workaround"?

@joslat, I went with the simple approach of exposing all my libraries as nuget packages. It sucks, but they're only internal packages, so it could be worse.

I decided to go for that approach, because it's very simple, and there's less that can go wrong.

I believe @matthewjones555 is the only solution that might workโ€ฆ that said I ended up adding Project references to the solution as it is only a single Project, so this works.
Tried to:

  • solve this in the project file, did not work.
  • create a custom nuspec and create the package, dlls were included in nuget package but could not add a reference to it or it could not resolve the references... found no documentation there... but looked good using the "nuget package manager" and they were placed in the right location...

So, no nuget as adding a reference works well.. at the moment this functionality is just needed in a single solution..

Please add it

Want to add my two cents: This is a small enhancement of the code that you guys (Thanks to @rohit21agrawal, @jr01 and @kkm000. ๐Ÿ˜Š) provided (luckily). It does take regard of sub folders that is relevant especially for satellite assemblies. Here the few lines:

<Project>
  <PropertyGroup>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>

  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
    <ItemGroup>
      <!-- Filter out unnecessary files -->
      <_ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))"/>
    </ItemGroup>

    <!-- Print batches for debug purposes -->
    <Message Text="Batch for .nupkg: ReferenceCopyLocalPaths = @(_ReferenceCopyLocalPaths), ReferenceCopyLocalPaths.DestinationSubDirectory = %(_ReferenceCopyLocalPaths.DestinationSubDirectory) Filename = %(_ReferenceCopyLocalPaths.Filename) Extension = %(_ReferenceCopyLocalPaths.Extension)" Importance="High" Condition="'@(_ReferenceCopyLocalPaths)' != ''" />

    <ItemGroup>
      <!-- Add file to package with consideration of sub folder. If empty, the root folder is chosen. -->
      <BuildOutputInPackage Include="@(_ReferenceCopyLocalPaths)" TargetPath="%(_ReferenceCopyLocalPaths.DestinationSubDirectory)"/>
    </ItemGroup>
  </Target>
</Project>

Edit: Just a bookmarkable reminder: https://gist.github.com/teroneko/7d61ee60a7c1ac15eb4272d54d567fa8~~

Edit 2.: I thought I just release a nuget package (how ironical) that does exactly the same like the code above. It does work right of the box, but you only have to assign PrivateAssets="all" to those projects you want to have included in your package. You can get the package at Teronis.MSBuild.Packaging.ProjectBuildInPackage and grab the sources here.

@teroneko Fantastic, exactly what I was looking for - I have a shared project with ResX localization files where each language becomes a separate DLL during build, in it's own subfolder. The previous solutions didn't take the subfolder into account and tried to put everything into a single location in nupkg, which obviously failed. Thanks again

It's literally a new decade and not only is this issue unfixed after over 3 years, there remains dead silence from the NuGet team on it. Looking at the MS commit history for the past year it's obvious why. Sad and disappointing that Microsoft isn't willing to make the packaging side of dotnet work like it should, I guess selling Core to the masses is more important than maintaining existing workflows.

@IanKemp You are literally basing your opinion on the commit history of a repository with no code in it.

I'd love for this issue to be resolved as well, but your take on this is hot garbage.

People are understandably frustrated by this, because it has been so long since the issue was opened, and I think it would be fair to say that the team seem reluctant to fix the problem.

It's important that we let them know about our frustrations in a constructive and respectful manner. It would be great to have an update from the team about this issue, and perhaps some guidance as to what we can do to finally get some traction on it.

I would like to join the frustrated group and kindly ask MS to do something with this issue!

I have also put up a simple demo solution here illustrating the problem quite cleanly, IMHO. It is simple, but it reflects a real-world scenario I am currently facing on one of my projects...

it's open source so anyone could still have a go at it.. Or find someone that could implement it and sponsor their time.

There are quite a few workarounds in this thread, they're just a bit hard to find between the "i want this as well" comments.

Don't get me wrong though, it's good to see comments of people that are interested. The hard part is understanding the "why" and acutal use cases behind them.
Those having read my comments know that i don't think including project references / multiple assemblies is something to broadly recommend doing. - It's a two-sided sword.. I've spent quite some time investigating issues resulting from it (dependency closures, assembly resolving errors during build or runtime, dependency management complexity and so on) and i've really only seen a handful of good use cases - most of the instances i've seen it was poor solution architecture resulting in the nupkg just being a hack around that (e.g. projects with interface definitions that were mostly useless anyway, but someone thought it was a good idea to make a project with all the interfaces and then include the DLL in multiple nupkgs). Please do prove me wrong and show me use cases where this makes a lot of sense.
I personally just think it's enough to have workable workarounds invoving msbuild logic or nuspec files instead of a first-class feature.

@dasMulli Please explain a better architecture for this:
You have a bunch of microservices written in C#. Each Service publishes an API using Nuget Packages, that other services can use. Now I want to reuse the Models used in the service and the service client as well. The best solution is three projects, a server project, a client project and a model project.
But I do not want to publish the models as its own package.

This is where I'd ask: "what's the problem with shipping 2 NuGet packages: Model and Client?". Especially now that we have transitive package references, you'd still only reference the client package and not even notice that there is another one involved. (unlike previous versions of NuGet where you needed to be careful to add all packges to all projects where they were needed)

Cons:

  • A bit more disk usage.
  • Two nodes show up in the dependency tree.

(Is any of these really an issue?)

Pros:

  • Model package can also be used in places where you don't need the actual client: e.g. Business logic processing the models, test projects.
  • You can easily create a .Server package as well if needed, e.g. if you're deciding that the API can be hosted by two different service implementations (microservice: keeping a legacy service and a new one around with the same APIs during a transition period). If both packages contained the DLL in a different version, you can risk funny issues, but you might not notice it b/c maybe a feed is missing a package. If the models are in one package, then package and assembly resolution will resolve one "true" version or error out with unsatisfiable package version constraints.
  • You can model data and API versions differently if you wanted to. Not thinking this is a good idea either but it's possible.

This is where I'd ask: "what's the problem with shipping 2 NuGet packages: Model and Client?".

This is where I'd respond: "What if I just don't want to? Why do I have to conform to a particular opinion of how the world should be, versus how my dependencies are currently structured or how I want them to be structured? Most importantly, why is this scenario - that previously worked just fine with older tooling - no longer supported?"

It's pretty much clear at this point that for whatever reason, Microsoft isn't interested in officially supporting this feature going forward. It's also clear they aren't willing to outright admit that, because it would make them look like chumps for telling everyone to switch to the "new and improved" dotnet tools, when some of those tools (i.e. dotnet pack) are actually _less_ capable than tools they are supposedly replacing (nuget.exe).

Real world example: The task is to implement logging library that should support ASP.NET classic and Core applications running on Framework 3.5 โ€“ 4.6 and .NET Core 2.1 โ€“ 3.1 (the actual functionality is not important for this discussion). The library should be delivered as a NuGet package for referencing by the various ASP.NET applications. The design was as follows:

Gdpr.Logging.Core project multitargeting net35 for use by legacy ASP.NET Framework applications and netstandard2.0 for ASP.NET Core applications. Defines IFileLogger logging abstraction + concrete LoggingPolicy implementation using it.

Gdpr.Logging.NLog project also multitargeting net35 and netstandard2.0. Has project reference Gdpr.Logging.Core and exposes IFileLogger implementation using the open source NLog NuGet package.

Gdpr.Logging.Target.Netstandard20 project - netstandard2.0 library with custom Microsoft.Extensions.Logging.ILoggerProvider implementation using types from Gdpr.Logging.Core and Gdpr.Logging.NLog (project references). This should generate NuGet package with netstandard2.0 builds of Gdpr.Logging.Core.dll and Gdpr.Logging.NLog.dll.

Gdpr.Logging.Target.Net35 - net35 library with custom System.Diagnostics.TraceListener subclass also using types from Gdpr.Logging.Core and Gdpr.Logging.NLog (project references). This should generate NuGet package with net35 builds of Gdpr.Logging.Core.dll and Gdpr.Logging.NLog.dll.

Codemap image here

Because of the limitation mentioned in this issue, such an architecture is not possible without quirky workarounds (I have ended up linking all source files from Gdpr.Logging.Core and Gdpr.Logging.NLog into the Gdpr.Logging.Target.* libraries).

@dasMulli it is perfectly reasonable to have private projects that you need to reference, that you don't want to expose as a standalone nuget package. It seems strange to claim that this isn't a valid approach.

I want to make my packages as friendly as possible to consume, and I don't want the end user to be exposed to implementation details of my architecture. In this case I don't want to expose any packages that aren't intended to be consumed standalone, as they will just cause confusion.

There are plenty of packages on the nuget gallery that get this wrong, e.g. here.

Redistributable components for package 'PostSharp'. This package should only be installed as a dependency.
(This is not the package you are looking for).

Having packages like these shouldn't be necessary. It's exposing an implementation detail that the user simply shouldn't need to care about, and risks users consuming your packages in an unsupported way.

IMO, we should be aiming for the most friendly solution possible, not the current "all or nothing" situation, with messy hacks if you'd prefer a middle ground.

To the logging example:
So we're looking at 4-5 nuget packages: "Core" which can be used by any net*/netstandard2.0+/netcoreapp2.0+ project, e.g. if i as a customer would want to implement Serilog support. Then an "NLog" package that uses the core with NLog and an ".MicrosoftExtensionsLogging" package that basically sets up NLog with the Core to integrate with Micrsoft.Extensions.Logging and a basic ".ClassicAspNet" package that sets up NLog with the core and registers a trace listener for classic ASP.NET Event.

On a different note, please don't assume that netstandard always means ASP.NET Core and net* always means classic ASP.NET. Both packages targeting .NET Framework can be restored for .NET Core projects and .NET Standard packages can be used on .NET Framework projects (with sufficient target framework version). A few packages went that route during the ASP.NET Core 1 time frame and quickly figured out that would become problematic - this is why we have Swashbuckle and SwashBuckle.AspNetCore (although i think they no longer share code as one has basically become a fork). One may also want to use Microsoft.Extensions.Logging on .NET Framework (i was doing that for a while).

So if you want to not ship a Gdpr.Logging.Core package, you risk:

  • Funny dependency issues if one references a new NLog package and an old classic ASP.NET package where there may be breaking changes in the core dll. There won't be any NuGet errors, just an MSBuild warning.
  • Someone externally to the project may have trouble using a different platform - e.g. Serilog - if there isn't a .Core package to start from.

So, if i'm not mistaken it should be possible to do this by shipping 4 packages and then have NuGet's package downgrade errors and/or dependency version ranges warn you of breaking changes.
This is similar to NLog's structure:

  • NLog.Web.AspNetCore -> NLog.Extensions.Logging -> NLog
  • NLog.Web -> NLog

As someone who is just uncovering this problem, it seems really strange and unnecessarily dogmatic that I'd have to publish multiple packages for APIs that will only be used for one project. They are not intended to extended or used outside of this single project. Furthermore, Microsoft has clearly shown that a single package is a good solution in some cases, with their inclusion of the new PublishSingleFIle command, and the fact that the nuget cli supports this exact feature for framework. It is not a technical issue, but purely one of implementation.

@dasMulli you're giving a strong opinion. I think it's clear some of us don't share that opinion or view it as a valid solution. At this point the question is simply, will the NuGet team continue the existing functionality of the nuget CLI tool into the dotnet CLI tool? dotnet 3.0 was a serious push to backfill existing APIs, perhaps the dotnet tools needs that as well.

Redistributable components for package 'PostSharp'. This package should only be installed as a dependency.
(This is not the package you are looking for).

Even Microsoft is guilty of this stupidity. The dependencies of Microsoft.Data.SqlClient includes Microsoft.Data.SqlClient.sni and/or runtime.native.System.Data.SqlClient.sni, both of which are marked "Please do not reference directly". It blows my mind that someone at Microsoft actually thought (well, they evidently didn't) that shipping packages containing these pleas was an acceptable way to address the issue of dependencies that should be private.

Imagine, if you would, that packages could actually include entire libraries inside themselves, instead of fragmenting the NuGet ecosystem into an NPM-like hellscape. One day, perhaps, humankind will achieve such a technological pinnacle. But that day is not today, not with dotnet pack.

@teroneko When I use your nuget package in my solution with 3projects, I get the following error:

FileNotFoundException: Could not load file or assembly 'Microsoft.Data.SqlClient, Version=1.10.19324.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5'.

This happens when I add PrivateAssets="all" on my project references. If I remove PrivateAssets="all", it works. Do you have any clue why this happens?

@Bryankarlsson One problem I see, when using this approach the package I offer does provide is, that all project references, that are marked with PrivateAssets="all" are copied to the package. So it can happen that unwanted projects, i.e. those products (dll's and what so ever), are also copied to the nuget package. It may happen, that the now local dll's, that are now present in the package are preferred, what can lead to assembly version missmatch. Can you reproduce this behaviour in an example repository, so that I can have a look at it?

Edit: @Bryankarlsson Mhh, I think the problem you describe are transitive dependencies which are not added the to public project. You have to add all dependencies that your third-party project does have to the target/public project. The target/public project is the project where your third-party project is marked with PrivateAssets="all". You have to imagine, that the dependencies from the third-party project are automatically forwarded to the target project, which are need to work with the dll from the third-party project. But when you mark the third-party project with PrivateAssets="all" in the public project, the transitive dependencies (for example Microsoft.Data.SqlClient) are not added to the public project, the project you want to share by package.

To add another Real World(tm) example, I'm wanting to create packaged templates that use several projects to enforce a specific architecture from the get-go, and contain xUnit and Azure deploy projects to save a pile of time for bootstrapping similar projects.

I can probably work around this with a shell project file, but that feels hacky as hell.

@furyd - have you seen "dotnet.exe new *" command to help create projects. This is a project templating mechanism.
They will eventually also be available as VS templates too... https://twitter.com/rrelyea/status/1228350573738463232

@furyd - have you seen "dotnet.exe new *" command to help create projects. This is a project templating mechanism.
They will eventually also be available as VS templates too... https://twitter.com/rrelyea/status/1228350573738463232

Hi @rrelyea , yeah, been using them for a few months and about the time you posted figured out how to get a multi project one done, albeit in the hacky way I mentioned.

I've essentially got a solution with all the projects I want in (api/domain/service/repository/deploy/test) under the src and tests folders, then in the parent (where the sln file lives) another project which has all those projects as content, along with the .template.config file, and it's that project I pack.

It all packs nicely into a nupkg, and installs as a template I can then instantiate via dotnet new , but it seems crazy I need that extra project!

4 years later, is this on any roadmap at all?

4 years later, is this on any roadmap at all?

Yes, at _Our Dreams_

Mhh, I think the problem you describe are transitive dependencies which are not added the to public project. You have to add all dependencies that your third-party project does have to the target/public project. The target/public project is the project where your third-party project is marked with PrivateAssets="all". You have to imagine, that the dependencies from the third-party project are automatically forwarded to the target project, which are need to work with the dll from the third-party project. But when you mark the third-party project with PrivateAssets="all" in the public project, the transitive dependencies (for example Microsoft.Data.SqlClient) are not added to the public project, the project you want to share by package.

@teroneko - We're running into exactly this issue in a project and are trying to find a way to add these dependencies automatically rather than having to remember to add them manually. Any thoughts on how to do that easily?

Can't believe microsoft doesn't have the resources to fix this problem

I am the co-maintainer of FluentMigrator (3M+ downloads) and this issue has haunted me for the longest time but I never knew about this issue until now. What sucks is that I broke release 3.2.4 due to a small change in the build process, and had to delist 20 packages as a result of a bad release.

@teroneko - We're running into exactly this issue in a project and are trying to find a way to add these dependencies automatically rather than having to remember to add them manually. Any thoughts on how to do that easily?

This is a good idea, but the question "how to do that easily?" and MSBuild are known to be tricky :) I tried Googling "how to pre-process a PackageReference" and the closest idea I could find was: https://stackoverflow.com/questions/6832069/msbuild-preprocessor-define-for-all-dependencies-of-main-exe-project but _I really don't want to write MSBuild targets using aspect-oriented programming style logics_.

That said, I found this blog post to be rather good solution: https://dev.to/wabbbit/include-both-nuget-package-references-and-project-reference-dll-using-dotnet-pack-2d8p

BTW, I think @dasMulli is pretty close to right on his comments about Client/Server. The .NET Runtime Packs have this same design challenge, If you squint, FrameworkReference is really solving the same pros/cons @dasMulli lists. Nick Guerrera at Microsoft wrote a dotnet design doc about FrameworkReference, available here: https://github.com/dotnet/designs/pull/50 - it has not been merged but it is more or less official. The problems are not exactly identical, but the key concept here is Representation Independence. The Pack and Publish steps need to be able to have unique models of the assets. In other words, I should be allowed to have a private dependency, as opposed to a public dependency.

Private dependencies sound like a good solution. However to make it rock solid, that does mean that those dependencies should be placed in a package specific folder, so you cannot accidentally get conflicts with other packages that have the same dll. I havenโ€™t thought out what the implications of that is.

Perhaps those dependencies should then be embedded in a single dll, and the feature should really still be limited to project references.

Being able to support different versions of the same but incompatible package would be pretty helpful for legacy software as well though - could be great being able to update package dependencies piecemeal.

Alternatively nuget packages should be prohibited from breaking backward compatibility. But those are different problems.

It will be tricky to find a good balance.

Private dependencies sound like a good solution.

Nuget.org packages already have "private dependencies". Which are irritating as hell. How can a package I know has dependencies not list dependencies? What nuget.org "Dependencies" really mean are public dependencies. What I'm advocating for is structure around private dependencies. Similar to a private class variable. You shouldn't depend on these private dependencies, but you should also have nuget.org tell you they exist.

Perhaps those dependencies should then be embedded in a single dll, and the feature should really still be limited to project references.

No. A single dll is an arbitrary limitation and likely will cause further issues. If Microsoft is going to spend time fixing this, they might as well get it right and not create a bunch of confusion that will cost more maintenance costs on the backend.

All I need is to package the the dependency artifacts of a project where everything is in the same solution.

So many people here want this feature for their own purposes. Is it so hard to implement this simple solution instead of defending some dubious dogmas?
Just do it!

@dasMulli here a real example for this feature:
Imagine some microservices, implemented in Clean Architecture style. One microservice (M2) communicates to another (M1) through REST using Refit. So I want to put IM1Client interface for Refit into separate project like M1.Client and build nuget, which will be used by M2.
M1Client uses shared DTOs which are placed in other project like M1.Contracts which is referenced by other projects inside M1. No other project is needed in M1.Client, so merge M1.Client and M1.Contracts into one project is definitely a bad idea.
So I need to build nuget package with both M1.Client and M1.Contracts dlls and use it for M2 and other services, which should communicate with M1. And I do not want to build separate M1.Contracts nuget package, because nobody will reference it, without M1.Client.
I'm sure, this is really useful case, when I do want to just use the requested feature, instead of spending hours in stackoverflowing for some workarounds.

Hi!
I've created a small tool that allows to pack a project with all its dependencies into a set of packages with the same versions.
https://github.com/btshft/ProjectPack. Not an ideal solution, but at least now I don't need to manually repeat package process for each of my dependencies including transitive or cross-project dependencies.

Such a shame - one of the most basic scenarios is still not fixed years later.

I've also hit this problem quite a few times. In some cases it is appropriate to expose the p2p reference as a separate nuget package, but in others it isn't. Please allow authors the ability to configure this behaviour.

One more person here running into this issue 4 years after the fact!

@JonDouglas looks like you're on the nuget team at MS, or may at least be able to point this to Chris Gill or someone else on the team. Any potential updates on where this stands?

While it may be better to create separate packages in many instances, clearly there is a frequent need for combining everything from time to time. Would be great if you guys could trust us to make a smart decision when it's appropriate.

@pdevito3 I'm totally with you! For the life of me I don't get why this isn't possible. I have a tool with dozens of assemblies that are not mine that I want to package, and it is just completely unreasonable to expect me to create a single package for each one! Just makes no sense.

I've been looking into this scenario and collecting feedback on what this would look like.

So far I've more/less heard that people want a property on <ProjectReference> called something like IncludeOutputDll which would be true/false & would result in the behavior as outlined in https://github.com/nuget/home/issues/3891#issuecomment-377319939 which will copy the project references into the package.

What this might look like:

  <ItemGroup>
    <ProjectReference Include="..\3891.Reference\3891.Reference.csproj" IncludeOutputDll="true" />
    <ProjectReference Include="..\3891.Reference2\3891.Reference2.csproj" IncludeOutputDll="true" />
    <ProjectReference Include="..\3891.Reference3\3891.Reference3.csproj" />
  </ItemGroup>

Any other proposals others may have towards this issue? Feel free to drop them below this message or jump on a call with me directly!

https://aka.ms/talktonuget (NuGet - Experience)

@JonDouglas I'd also like to be able to include arbitrary dlls and executables that are not referenced, or are not a nuget package.

However your IncludeOutputDll attribute is a great start!

I'd say this has to be smarter. In NuGetizer this is automatic. If the referenced project declares a PackageId, it's added as a dependency. If it doesn't, its contents/outputs are included in the referencing project package. To exclude that behavior, you just set Pack=false on the project reference.

I think the default behavior should match what you'd typically do, and extra metadata should serve for the exceptions when you want to deviate from the more obvious behavior. In this case, I'd say the current behavior is not really intuitive and having to explicitly opt-in to the more obvious behavior seems odd.

@kzu Just looked at NuGetizer - looks really cool. I'm going to give it a try next time I need to package something.

@JakeSays For those who don't know, Microsoft pretty much just does whatever @kzu comes up with, but it takes about 3-4 years from his prototype to become a reality. Literally everything you see in .NET Core was prototyped by him in the old days. Just search nuget.org for his author name and you will see what I mean. Worth seeing what he has in the oven.

Any other proposals others may have towards this issue?

@JonDouglas I already said what I think is needed - more structure around private dependencies. .NET is not JavaScript (where object encapsulation == module encapsulation, so literally every object tree can be encapsulated), so unless developers consciously load an assembly and its dependencies in a new AssemblyLoadContext, all private dependencies are in effect public. You can see a conversation I had with @terrajobst about this yesterday: https://github.com/dotnet/standard/issues/859#issuecomment-700161163

If the referenced project declares a PackageId, it's added as a dependency. If it doesn't, its contents/outputs are included in the referencing project package. To exclude that behavior, you just set Pack=false on the project reference.

@JonDouglas This is a much better behavior than your proposed specification. I don't really like flags in my build file, I want settings that select what target items belong to. That's the MSBuild way, and it works really well when followed religiously. IncludeOutputDll isn't associated with any particular action, so it's a pretty confusing API. Include,... _where_? Not trying to give you a hard time, just some guiding principles if you're newer to MSBuild. The core idea is that a target is a goal, and you want the build system to reach it for you, without telling it exactly how to do that.

@jzabroski I just want to move the conversation to a solution forward. My proposal is merely trying to start to understand what a solution would look like from an end-user perspective. If you do have a proposal you'd like to throw on this issue as a comment, that will also help us weigh our options as well.

@JonDouglas I think that something like what @kzu mentioned would actually make a lot more sense.

<ItemGroup>
    <ProjectReference Include="..\3891.Reference\3891.Reference.csproj" IncludeOutputDll="true" />
    <ProjectReference Include="..\3891.Reference2\3891.Reference2.csproj" IncludeOutputDll="true" />
    <ProjectReference Include="..\3891.Reference3\3891.Reference3.csproj" />
  </ItemGroup>

While this might be very explicit, I think it makes a lot more sense simply to evaluate the reference... if it was packable or not. Where this explicit option makes more sense IMO is where you may want to force the issue like if for some weird reason you wanting ProjectB to a standalone package and the assembly included as part of ProjectA without the nuget dependency...

@dansiegel Care to elaborate with an example of how that would look as a sample? Thanks!

Sure... so let's say we have Project B and it's csproj looks something like this

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <IsPackable>false</IsPackable>
  </PropertyGroup>
</Project>

Implicit

For an implicit scenario let's say we have Project A and it's csproj looks something like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <IsPackable>true</IsPackable>
  </PropertyGroup>

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

Explicit

For an explicit scenario let's say we have Project A and it's csproj looks something like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <IsPackable>true</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ProjectB\ProjectB.csproj" Pack="true" />
  </ItemGroup>
</Project>

The pack task should simply understand that ProjectB wasn't packable and know to include the assets. IMO it's a pretty simple task to determine we should pack reference DLL's the real trick is where to place them.

Real Life Scenario

As you know there are a number of sub-folders within the package that dlls may potentially live depending on what the resources are... for a little more real life here let's take something that I would actually want to do... Let's say I have 5 projects like the following:

  • AwesomeProject
  • AwesomeProject.Analyzers
  • AwesomeProject.BuildTasks
  • AwesomeProject.Reference (IsPackable = false)
  • AwesomeProject.External (IsPackable = true)

In this more complex scenario, it's about more than just simply that I need to pack multiple dll's into the NuGet package

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <IsPackable>true</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\AwesomeProject.Analyzers\AwesomeProject.Analyzers.csproj" PackageRoot="analyzers" />
    <ProjectReference Include="..\AwesomeProject.BuildTasks\AwesomeProject.BuildTasks.csproj" PackageRoot="build" />
    <ProjectReference Include="..\AwesomeProject.Reference\AwesomeProject.Reference.csproj" />
    <ProjectReference Include="..\AwesomeProject.Reference\AwesomeProject.External.csproj" />
  </ItemGroup>
</Project>

For this sample we're implicitly referencing these external projects... in the case of AwesomeProject.External we would expect a NuGet dependency.. while AwesomeProject.Reference would be included as a lib resource, and we would then expect our Analyzers and BuildTasks to be in their respective folders.

Hope that helps... always happy to jump on a Teams call and walk through some current projects to detail more of how this would be useful

@JonDouglas Just copy what @kzu does in Nugetizer. Half of the modern improvements to MSBuild syntax are inspired by stuff he has created for the community.

I get that you want to check things as done, but you can't lead by creating solutions that dive too far into the details.

IncludeOutputDll=true does not:

  1. Specify where to output the dll
  2. Specify what action creates the output
  3. Assumes we only want to output dlls
  4. Is it a private or public dependency in the nuget package.

Separately, this doesn't even address the fact that private dependencies have no "isolation" properties/structure around them. It's assumed you just want to dump the dependency to some "Output".

โŒ

<ItemGroup>
    <ProjectReference Include="..\3891.Reference\3891.Reference.csproj" IncludeOutputDll="true" />
    <ProjectReference Include="..\3891.Reference2\3891.Reference2.csproj" IncludeOutputDll="true" />
    <ProjectReference Include="..\3891.Reference3\3891.Reference3.csproj" />
  </ItemGroup>

โœ”๏ธ
https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#including-content-in-a-package <-- MSBuild already does this, we're talking about a very, very specific edge case where the product does not function correctly, because ProjectReference(s) are the composition of this feature AND the NuGet transitive dependency / "resolve frameworkreference magic" feature

@dansiegel Thank you kindly for the example!

@jzabroski Thank you for the suggestions! Again, don't think that my proposal has any merit to it, it's merely a conversation piece to get to the deeper understanding of an ideal solution as pointed out by yourself, @kzu, and @dansiegel. We'll work to create a spec based on discussions here & get the community's eyes on it to progress forward.

I mean, IncludeReferencedProjects is already a thing, you do one CLI switch and it goes. I'm pretty sure I've used that and it worked as I expected... I would base it off of that.

IMHO if a referenced project itself is set to create a package, it should be treated as a package dependency, otherwise it should include the referenced project's DLLs directly. I really would rather not have to do something manual for each project.

Honestly I would change the default behavior, which seems completely wrong/unexpected to me and everyone I've talked to about this at work. Since that's unlikely, add an <IncludeReferencedProjects/> tag. Personally I typically use the package build checkbox in VS, which only adds <GeneratePackageOnBuild> and neither <PackageId> nor <IsPackable>.

Edit: In one week it will have been two years since my last comment on this; I had given up. It's nice to see some momentum to fix this.

@jzabroski I didn't find your "for those..." comment useful. Surely you have more constructive things to say.

@JakeSays I'm sorry you didn't find it constructive, but I was just communicating that every time I've run into a problem with MSBuild, I've found that @kzu has a solution for it way before Microsoft has an official solution. I feel that's a very constructive tip to add to this discussion. I have built a fair amount of my career by figuring out "who to follow" and being good at taking direction from people smarter than me. I thought, by sharing that with you, I was endorsing more than just Nugetizer but also @kzu as someone you can generally trust to keep you on the cutting edge and building better, easier to maintain software.

Honestly I would change the default behavior

Basically, if you squint, .NET Global Tools _already_ change the default behavior in the way we're discussing.

See: https://www.nuget.org/packages/FluentMigrator.DotNet.Cli (notice it says no dependencies)
See the raw file via fuget.org to see all the private deps: https://www.fuget.org/packages/FluentMigrator.DotNet.Cli

But, if I want to package a .NET Framework legacy tool, like FluentMigrator.Console, so that people on net461..net48 can run a "Global Tool" on a legacy platform, I have to jump through hoops to do that.

I guess I don't understand why this is so complicated when _I know_ MSBuild supports it. I think it's just one of those issues that everyone forgot to do and saw a really long discussion so they assumed there were many milestones left to get this over the goal line, when in reality this was probably deliverable some time when .NET Core 2 introduced Global Tools.

I agree that IncludeOutputDll="true" won't be good enough. It basically means everything (true) or nothing (false). What means that you'll put a lot of assumptions into the implementation what would work for as many as it would not.

Which DLLs will be in included, which not? Only project references recursively, or binary references too? How you'll distinguish the dependencies coming from a NuGet package? From the Framework (e.g. System.*.dll)?
What if a project has a project reference to another one that has a binary reference on my own System.Fancy.dll, would it be included?

What I'd like is to be able to explicitly include or exclude some DLLs by name (using the MSBuild built-in mechanism of includes/excludes).

What I'd like is to be able to explicitly include or exclude some DLLs by name (using the MSBuild built-in mechanism of includes/excludes).

Yes, this. Again, MSBuild _should_ already support all this. _Ideally_, since this is being driven by @JonDouglas who is Senior Program Manager now, we would _also_ get some nuget.org improvements, such as splitting out public and private dependencies on the package page, so people can actually see things like, "This is a .NET Core Global tool that targets netcoreapp3.1 TFM". _That_ is the real value add I would love to see (sorry if my earlier comments about private dependencies weren't so clear - I should have given a concrete example like I just did with FluentMigrator.DotNet.Cli above).

Reading between the lines I seem to have found this (undocumented otherwise?) property TreatAsPackageReference which seems like it would turn the weird built-in behavior into what nugetizer does, but requiring the user to be explicit ๐Ÿค”

It's so undocumented it doesn't show up in GitHub Search anywhere in the dotnet org or Nuget org https://github.com/search?q=org%3Adotnet+TreatAsPackageReference

But it does show up in NuGet, in particular this code search: https://github.com/search?q=org%3ANuGet+TreatAsPackageReference&type=code reveals

https://github.com/NuGet/NuGet.Client/blob/bf6a3109391ae765343e3fa5d10711ccef128a2d/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs#L865-L866

which references https://github.com/NuGet/Home/issues/3891 (THIS ISSUE, haha, I just did an Inception)

Oh gosh... Meanwhile, I added transitive private assets inclusion too, via project references or package references alike to #nugetizer ๐Ÿ˜. Pack=false (on either ProjectReference or PackageReference) stops the inference.

Thanks for doing that, @kzu . In my opinion, the key is that @JonDouglas lay out a plan to make nuget.org package pages less confusing so that private dependencies are visible when people look at a package. Do you agree, @kzu ?

Are there any other user stories where the private dependencies are confusing on nuget.org?

Side note: I understand that some dependencies are deliberately obfuscated so people aren't confused, mostly ones suffixed with *.sni in the package name. I must confess, I don't even know what *.sni refers to in Microsoft-land. I imagine it's something like "system native image" and a platform-specific ngen'd binary.

Hm... I'd say surfacing those private dependencies is more of an advanced use case, rather than something I'd find confusing myself. Consider a build-only or analyzer-only package. That will typically use whichever private deps it needs to do its job, say Newtonsoft.Json. There is no reason why those private deps should be of concern to me: they only apply during my local dev build environment and will never ship with my product.

In the case of libraries, that's different, granted. They are exposed in the IDE for exploration, btw, so maybe it's a matter of surfacing that too somehow in nuget.org? I'd say it's a bit of an advanced scenario though...

image

I'd say it's a bit of an advanced scenario though...

I guess that's why I ask others - I do this a lot, and I don't know if I've just developed weird habits, or if others are similarly confused when they check nuget.org for dependencies and see nothing, even though it's clear that including a dependency will add something to my build output.

As an example, when you have what I call a "public" dependency (what NuGet / MSBuild teams call transitive package dependencies), it's listed on nuget.org and the tooling will automatically use RollForward technology to smooth over minor assembly version differences by generating a deps.json file that allows dotnet.exe pass to the CLR range of assembly versions it should redirect to. However, when you have a private dependency, such as a .NET Core Global Tool transitive dependency, you effectively "opt out" of this magic, which is tragic. I believe this is the main reason why this feature has not been addressed in .NET today, because it fundamentally subverts the FrameworkReference magics, because you "cut" the transitive dependency list by creating private dependencies. That's why I referenced this comment: https://github.com/dotnet/standard/issues/859#issuecomment-700161163 In most cases in this thread, people are statically linking to the ProjectReference, so MSBuild would be able to correctly generate the deps.json. When you are not "compiling at head globally" (as Jon Skeet calls it), things get trickier.

Once the package you created gets re-imported by another library, you get problems. In the PackAsTool case, we know you're generating an Executable (called a Tool), so we don't really care that we chopped the dependency graph _in most cases_. The _only_ case where it stinks that the graph is chopped and not discoverable is if your Tool loads _plug-ins_ that need to inter-op with the Tool itself _or_ if the plug-ins transitive dependencies conflict with the Tool's transitive dependencies (because there is no mechanism to RollForward because the metadata to RollForward on is elided when you chop the graph).

@kzu Thinking out loud - to your point about advanced use cases vs. solving immediate problem - I wonder if the immediate solution should be:

  1. Do the quick and dirty fix - don't try to solve the reference flows.
  2. Create an NuGet MSBuild warning when you do this that you might have transitive reference failures under some conditions.

@jzabroski I can only see this as a problem for that scenario you mention about a dotnet tool that loads plugins. That seems like an awfully corner case, TBH. I don't see it happening on build tasks/targets cases or perhaps even analyzers.

@kzu I don't know, I get bug reports about this stuff all the time, and for two very different projects. I think when most people run into these problems they just go do something else, because they can't figure it out and they have to ship product, so they hack until they find something else that works.

This issue is now most commented, with most like reactions, has been open for 4 years now and was last active yesterday.
It's clearly the most anticipated feature for NuGet currently.
Can you please give it a priority bump and pull it out of backlog?

I would keep it simple, and take these steps, in these order:

  1. Restore the original 'include project references' functionality. This means that if I choose one project in a solution and I want to package it, all other projects in that solution that have a project reference to this one should be included in the package.

  2. Keep only nuget references to projects that have been configured to produce a nuget package themselves within the specified solution.

  3. Add project settings to override or manually control this behavior.

I would definitely also consider looking at this from a solution level. Many actions, like testing, code coverage, nuget package building and such, make more sense to do from the context of a solution.

(And a completely separate thing - allow basic functionality to configure package versioning based on git branch. I always want a pre-release package from feature branches and release versions from master, say, and it's always a hassle to do this. But I'm guessing that should be a separate request.)

I agree with niwrA, this issue seems to be about replicating IncludeReferencedProjects, which I completely support. I'm starting to worry that this issue is expanding and getting complicated. The existing pack option functionality is pretty well defined, works, and covers most use cases I think. The version-git integration definitely seems like a separate GitHub issue, IMHO.

Maybe I'm misunderstanding the need to introduce private nuget refs to this issue (I've never needed to use them or had any issues with them AFAIK so I don't know much about them), but I think including a referenced project has nothing to do with creating nuget dependencies--it eliminates those dependencies from nuget's perspective. It seems to me that private nuget dependencies are the same as public ones in this scenario. The functional non-package-creating dependencies (projects) are _included_, so as an external artifact they disappear. They aren't a private nuget package reference or any kind of reference.

P.S. dotnet pack absolutely _does not_ perform the IncludeReferencedProjects behavior by default, and does not respect the package-on-build project element, unless it was changed recently. That is precisely how I found this issue two years ago. I was boggled by the default behavior and inability to change it.

Hey everyone,

First off, thank you all so much for weighing in on potential solutions for this missing experience.

From what I gathered, here is a summary I've put together:

  • nuget pack currently has -IncludeReferencedProjects
  • MSBuild supports <Pack> and IncludeContentInPack
  • dotnet pack does not support -IncludeReferencedProjects or respect existing MSBuild functionality today.

Behavior:

A built package will include referenced projects as dependencies or as part of the package. Resulting in referenced project dependencies into the .nupkg file.

Scenarios to support:

  • dotnet CLI
  • PackageReference
  • .nuspec & .csproj packing scenarios

There are a few aspects to this work:

  1. Understanding the flows for compiled assemblies. (This issue has a documented workaround already, but we still need to consider it)
  2. Understanding the flows for referenced projects.
  3. Understanding the flows for trimming dependencies when merging.

While we will be looking to create a proposal for everyone to provide an opinion in the short term, I'm also interested in hearing your thoughts until then.

  1. What is your expectation of this feature?
  2. What does it functionally do?
  3. What problem does it solve for you today?
  4. When would you like to see this feature?

Thanks again for the patience.

Is this happening ? When can we expect the -IncludeReferencedProjects feature to be released ? With my team, we are currently considering going back to using nuget.exe + nuspec files as we have come across this issue with dotnet pack and this is causing us a real headache.

@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 ...>.

This will add the referenced projects dlls to the generated package.
But if the referenced projects also have dependencies on other nuget packages, those won't be added to the current package, will they?

Hey everyone,

First off, thank you all so much for weighing in on potential solutions for this missing experience.

From what I gathered, here is a summary I've put together:

  • nuget pack currently has -IncludeReferencedProjects
  • MSBuild supports <Pack> and IncludeContentInPack
  • dotnet pack does not support -IncludeReferencedProjects or respect existing MSBuild functionality today.

Behavior:

A built package will include referenced projects as dependencies or as part of the package. Resulting in referenced project dependencies into the .nupkg file.

Scenarios to support:

  • dotnet CLI
  • PackageReference
  • .nuspec & .csproj packing scenarios

There are a few aspects to this work:

  1. Understanding the flows for compiled assemblies. (This issue has a documented workaround already, but we still need to consider it)
  2. Understanding the flows for referenced projects.
  3. Understanding the flows for trimming dependencies when merging.

While we will be looking to create a proposal for everyone to provide an opinion in the short term, I'm also interested in hearing your thoughts until then.

  1. What is your expectation of this feature?
  2. What does it functionally do?
  3. What problem does it solve for you today?
  4. When would you like to see this feature?

Thanks again for the patience.

I think it's pretty clear what people need ๐Ÿ˜. It's no longer time for questions, the requirement is in the title of the feature and it should have been implemented a long time ago already. Can you make this happen please?

Hey everyone,

First off, thank you all so much for weighing in on potential solutions for this missing experience.

From what I gathered, here is a summary I've put together:
...

Thank you! It's wonderful to see this issue is finally being taken seriously.

Here's my take on the feedback you've asked for:

  1. What is your expectation of this feature?

I'd like to easily create a nuget package that includes dependencies from projects that I don't wish to expose as their own individual packages.

So:

Package1.nupkg

Contains all binaries from:

Project1.csproj
Project2.csproj
Project3.csproj

  1. What does it functionally do?

Makes it a lot easier to create packages that have a mixture of private dependencies (that will be self contained in the package), and public dependencies (that will be exposed as other packages).

This is the largest pain point right now when creating nuget packages.

  1. What problem does it solve for you today?

It will allow me to get rid of pointless public nuget packages that should not actually be public.

  1. When would you like to see this feature?

This feature is long overdue. I understand you guys haven't been working on it, as it hasn't been taken seriously, but this issue is 4 years old.

It would be nice to see this work prioritised so it can be part of a release within the next year. Sooner if possible.

FWIW, I think this is a side-effect of a (very) bad default value for IsPackable in SDK Pack. It assumes anything you don't opt out of packing, is actually a package. And therefore everything ends up creating packages, everything ends up being dependencies instead of embedded assets, etc.

I'd change this to the way it works in NuGetizer: if you provide a PackageId, you're opting in to IsPackable=true. If you explicitly set it to true, also. But otherwise, nothing is packable and referencing those projects just ends up having them as embedded assets in the referencing (packable) project.

Then just set a property to get the legacy behavior if you depended in any way on that (i.e. LegacyIsPackableDefault or whatever).

@JonDouglas I think the one other scenario to support is a better nuget.org UI that let's people know about private dependencies. Effectively, calculate the flow, but don't list them as public dependencies. Why throw away the good data if you have it available?

Basically, for a small increase in package size, you get a wealth of useful information to build tooling around. As I've already explained, this metadata is essential for building Tools or apps that want to co-integrate with hot swappable plug-ins (runtime loaded assemblies).

As far as @kzu suggestion to go off PackageId, I think as long as the tooling is there in the Project Settings dialog in Visual Studio, then that is probably an improvement. I'm not a huge fan of adding a LegacyIsPackableDefault without also adding a target date to obsolete that flag. An alternative solution is to have Visual Studio update projects if the sln version is before this change.

Wow, a mere 48 hours before this thread enters its fifth year...

.NET 5, 5th year... Coincidence? I don't think so =D

@JonDouglas thank you very much for working on this. I would be so happy to see it available.

  • MSBuild supports <Pack> and IncludeContentInPack

Hello,
could you provide any example of usage for this 'IncludeContentInPack', can't find anything.

  • MSBuild supports <Pack> and IncludeContentInPack

Hello,
could you provide any example of usage for this 'IncludeContentInPack', can't find anything.

https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#includecontentinpack

Thanks for a quick reply @IanKemp. I was actually hoping, that it will allow MSBuild to also include referenced project (as nuget includeReferencedProjects property).
I have asp.net app migrated to packageReference and SDK, I want to pack all projects to one NuGet - can't use NuGet command for it as it is said in this document - that usingmsbuild -t:pack is recommended for projects migrated from packages.config.

How should I approach it? What workaround is best for this? Here I found some listed

Just posted on the mentioned StackOverflow a full repro and how it works already in NuGetizer with no changes to the project other than just adding a package reference. Just in case you need this right away and can't wait for a fix ๐Ÿ˜

I feel like this is still needed. A good example might be wanting to packet project reference dependencies into a Analyzer or Source Generator Nuget package. Currently I don't think there is a way to include the assembly files directly in the package like there is for private asset Nuget package references.

I needed to be able to produce packages (A, B) for two assemblies in the same branch where one of the packages (A) depended on another (B), but used a project reference in the solution. The way I made it work was to have a nuspec for A that had B in the dependencies section, but used a version of "SAME". The SAME value was replaced at build time with the version that was being applied to the packages and before they were packed.

I posted this with a bit more info as part of at least one issue if anyone needs it.

@HelloKitty with NuGetizer you can pull arbitrary files from arbitrary package references with:

<None Include="lib\netstandard2.0\Newtonsoft.Json.dll" PackageReference="Newtonsoft.Json" /> 

It would be awesome if SDK pack included that capability too, since I think for non-trivial scenarios like generators, tools and even build tasks, it's quite useful.

@StingyJack I'm thinking whether a ProjectReference="..." attribute could serve the same purpose for a project reference's packed content... If so, would you like to open an issue so I can work on it? Thanks!

@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 ...>.

This will add the referenced projects dlls to the generated package.
But if the referenced projects also have dependencies on other nuget packages, those won't be added to the current package, will they?

You are correct, this will add only the referenced project dlls and not the dependencies of other NuGet packages.
Does anyone have a solution for it?

For example:
Project A
|----> Project B

Project B
|----> SomePackage

We will get NuGet package that includes ProjectA and ProjectB dlls, but without the dependency to "SomePackage".

I think I know of a dirty hack:

  1. create a dummy project (no code on it)
  2. ProjectReference all the other projects you want (to ensure they get built 1st) before attempting to build the package.
  3. With the dummy project made mark all of the other projects non-packable
  4. have the dummy project target all of the TargetFrameworks of the other projects, they must then all match
  5. Add the following to the dummy project:
<ItemGroup>
  <_PackageFiles Include="[path to each project output path]\$(TargetFramework)\[project name here].dll">
    <BuildAction>None</BuildAction>
    <PackagePath>lib\$(TargetFramework)\</PackagePath>
  </_PackageFiles>
  <_PackageFiles Include="[path to each project output path]\$(TargetFramework)\[project name here].xml">
    <BuildAction>None</BuildAction>
    <PackagePath>lib\$(TargetFramework)\</PackagePath>
  </_PackageFiles>
  <!-- add reference assemblies optionally?. -->
  <_PackageFiles Include="[path to each project output path]\$(TargetFramework)\ref\[project name here].dll">
    <BuildAction>None</BuildAction>
    <PackagePath>ref\$(TargetFramework)\</PackagePath>
  </_PackageFiles>
  <!-- add docs to reference assemblies too?. -->
  <_PackageFiles Include="[path to each project output path]\$(TargetFramework)\[project name here].xml">
    <BuildAction>None</BuildAction>
    <PackagePath>ref\$(TargetFramework)\</PackagePath>
  </_PackageFiles>
  <!-- include pdbs and source code by somehow manually making an snupkg file using all of the above
       project information ^. -->
  <!-- any other build artifacts that you want to include in the AIO package like msbuild task files
       to an msbuild task project you optionally want to include with the other projects so consumers
       can use the task too. -->
</ItemGroup>

The issue is marking the dummy csproj file as something that must not be compiled but to basically only generate an nuspec to use for packing.
Just to generate a nuspec that properly contains the dependencies of each project, and everything.

Another option is to possibly manually add the dependencies to each project into the dummy project, then skip step 2 to ensure each project you want embeeded into the package does not sneak into the Dependencies of the single package, and basically have it in an standalone solution file that then is used to make the package.

Everyone is coming up with elaborate ways to avoid having to create a 15-20 line XML file (nuspec) that will do exactly as you instruct it to do.

What everyone wants is a tidy solution where they only have to specify things in one place. The problem with going back to nuspec files is they're a second source of truth, and can diverge from how things are set up in your projects. It's not a case of avoiding having an extra 20 line file, it's a case of avoiding having a file that can become stale with no easy way of automatically detecting the problem.

The nuget integration into msbuild is great, but it has some rather large holes, for what seems to be fundamental functionality. When you run into these problems, it makes the integration seem incomplete.

@StingyJack true that. I was also searching a lot and I find creating nuspec the easiest way.
Just maybe one question. Let's say we have project with quite a lot dependencies. After build, all the DLLs and files are being created in obj and bin files.
What I have found, that the easiest solution to create nuget is to just add this element, that would simply copy folder, that is normally being published as artifact.

  <files>
    <file src="obj\Release\Package\PackageTmp\**" target="" />
  </files>

With this one, we can replace using build artifacts with nugets (in example of azure devops).
Any comments on doing it this way? What can go wrong? :)

@roofiq - the obj folder is for temporary compilation and other transient stuffs, so its a bad idea to try to use anything from an obj folder. The binRelease folder is where you want to get the files if you are making a normal nuget.

Yer also missing a target value and are probably pulling in way too many files.

I'll usually use file elements like this so I can get the MyProject.DLL, MyProject.PDB, and MyProject.XML all packed up to support some debugging of the package and intellisense for VS. (make sure to set either "Full" or "Embedded" PDB for the Release configuration.)

<files>
    <file src="bin\Release\MyProject.*" target="net462" />
</files>

With this one, we can replace using build artifacts with nugets (in example of azure devops).
Any comments on doing it this way? What can go wrong? :)

You are making it hard for yourself in a few ways. You cant deploy a nuget package from a release pipeline with the easy to use, built in tooling. Also programs are not packages. Except if you are actually making Chocolatey packages (that use the nupkg format but are still not nuget packages) and using chocolatey to install apps. I dont get the appeal of using a nuget package format and all the rules around packing it instead of an artifact (a plain zip of the published projects from the build).

You probably also lose all of the artifact retention stuff that happens with a release pipeline by not using artifacts.

@StingyJack - thanks for the long answer ๐Ÿ‘
I know, that the final compiled files are in /bin folder. But I have investigated the actual SiteRoot folder and it was 1:1 as the one in /obj (.net framework 4.8 app). So I have decided to use this one. It actually worked.

You are making it hard for yourself in a few ways. You cant deploy a nuget package from a release pipeline with the easy to use, built in tooling.

Basically the point of my answer, was that we want to switch from using build artifacts to artifact feeds. So my task was to come up how we can easily pack project, that has mutliple dependencies.

So to sum up, if we would simply point bin/Release to be copied to Target directory specified in Nuspec file, it will be done in good practise?

@roofiq that depends on how your build setup is, if the build is complex where you need to package like:

  1. 10+ projects into an single Package, this being named your own Sdk.

The crazy shit is that somehow the files to the .NET Core SDK binaries and runtimes are all packed into a single large ass package, how the f**k did they do it when we cant simply replicate that shit.

This all feels like defacto b******t to me that they can do it, yet we cannot easily do the same. All I want is to provide all my projects into a single package instead of the 10+ packages they are now that I am now having issues with maintaining the shit and then trying to find time for other development work on other things as well.

But like man the hell is wrong with the fact that .NET Core can do this on their packages, yet we cannot.

There even lacks the documentation on how you can make your own dotnet sdk that one can install and then it basically then auto references the binaries you made as well as then providing the original .NET sdk references based on the TFM just by changing the 1st line in an csproj to your own sdk. It's a lot better than just downloading and trying to maintain 10+ nuget package as that becomes a single package, plus then could be concentrated into an single git repository, which then in turn can be used to not only save time but also be used to file issues for each project within the single repo, prs, and also dependabot would update every project's dependency at once compared to 10+ projects x amount of dependencies and then worry about 100+ pull requests and waiting over 5 hours just for them all to auto merge and crap when that time could be reduced to 10 minutes from only having a single repository and generating an single package instead that holds all of the compiled files (including symbol packages with everything).

So to sum up, if we would simply point bin/Release to be copied to Target directory specified in Nuspec file, it will be done in good practise?

@roofiq - Yes, but it is going to depend on if you are packaging assemblies or programs in this way. When you refer to _build artifacts_, those are usually going to be programs (Web Apps, Windows desktop apps, Web Sites, WPF, AWS Lambda, Azure Function/webjob, etc.) and sometimes other things like config files. Making these into nuget packages adds complexity because you go from a simple zipped build artifact that can be unzipped and xcopied for deploying to a nupkg (also zipped, but a container with a very specific format that you need to try to make your application fit into and then extract back out of). I'm thinking you may mean programs because the path that you were getting the files from looks like where Published apps go when you dont specify a publish path.

If you had been putting your dll's by themselves into build artifacts than the answer to your question is definitely Yes, point to the binRelease and pack/push. Just dont forget to generate and pack PDB's and XML comment files in there too (these are not required but this is far easier to do than to setup and configure symbol servers, add symbol publishing to a pipeline, then update every dev;'s visual studio instance to use the symbol server).

this being named your own Sdk

@AraHaan - I'm actually at a loss for understanding what you mean by "SDK", an example may help, but please open a different issue for what you need cos it doesnt seem aligned with this topic. Feel free to tag me. The large packages you see are actually metapackages and dont contain files.

Sad we are now on .Net 5 and this is still an issue... I do not see any progress on this...

I found something that works however it would need some edits to work for satellite assemblies too.

https://github.com/Elskom/Sdk/
Take a look at the dummy nuspec with an csproj just to make it all work with dotnet build / dotnet pack.

@AraHaan - those nuspec files are really, really over-worded.

Rather than tangent this thread further I'm going to open an issue on the repo you linked to share some ideas with you that may reduce your maintenance pains. I'll link it here for anyone who wants to follow.

Was this page helpful?
0 / 5 - 0 ratings