Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
785 views
in Technique[技术] by (71.8m points)

visual studio 2017 - MSBuild multiple dll in a single NuGet package

I have a Visual Studio 2017 solution that contains two projects:

Foo.csproj
Foo.Core.csproj

Both of these projects target multiple frameworks: net452;netstandard1.2

Foo.csproj includes a project reference to Foo.Core.csproj:

<ItemGroup>
    <ProjectReference Include="..Foo.CoreFoo.Core.csproj" />
</ItemGroup>

When I generate a NuGet package for Foo.csproj, I want the nupkg file to include both of these assemblies.

What is currently happening is that the NuGet package that gets created has Foo.dll and then a NuGet dependency on Foo.Core (which doesn't exist).

How can I generate a single NuGet package using msbuild that will include both assemblies?

For reference this is the command I am currently using (which is not working how I want it to):

msbuild /p:restore,pack Foo.csproj
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is currently not directly supported by NuGet out of the box. You can follow this GitHub issue for updates.

However, there are a few ways to create such NuGet package.

  1. Use the "Nugetizer 3000"

This is an newly developed tool to build NuGet packages from projects and works by installing the NuGet.Build.Packaging nuget package. You can find some documentation on it on its GitHub wiki page but since it is a very new project, there isn't much documentation or community knowledge around it yet(!) (but the team developing it is very helpful, you could file GitHub issues if you get stuck).

  1. Adding a custom target in the project (2.0.0 tooling / VS 2017 15.3+): Create an item in the csproj that will include the referenced project's output DLL

This approach is very hacky as it relies on an internal MSBuild item that the pack targets use. It works by first marking the <ProjectReference> to not be referenced from the created nuget package like this:

<ProjectReference Include="..libAlibA.csproj" PrivateAssets="All"/>

Then you can add this to the project to include the generated libA.dll in the nuget package:

<PropertyGroup>
  <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeP2PAssets</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="IncludeP2PAssets">
  <ItemGroup>
    <BuildOutputInPackage Include="$(OutputPath)estprivatelib.dll" />
  </ItemGroup>
</Target>

Note that this requires you to add all the <PackageReference> items of the referenced project to the project you generate the package from since they would be missing from the generated package since you effectively disabled the transitive reference behaviour.

  1. Create a custom .nuspec file

At the time of writing, this is probably the most "supported" way, but also the most complex. NuGet allows you to disable the automatic generation of the resulting .nuspec file and automatic collection of files by setting the <NuspecFile> property in your project, along with a <NuspecProperties> property that allows you to pass replacement tokens for parsing the .nuspec file.

This works by modifying the project file like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.4</TargetFramework>
    <NuspecFile>$(MSBuildThisFileDirectory)$(MSBuildProjectName).nuspec</NuspecFile>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..LibBLibB.csproj" />
  </ItemGroup>

  <Target Name="SetNuspecProperties" BeforeTargets="GenerateNuspec">
    <PropertyGroup>
      <NuspecProperties>$(NuspecProperties);id=$(AssemblyName)</NuspecProperties>
      <NuspecProperties>$(NuspecProperties);config=$(Configuration)</NuspecProperties>
      <NuspecProperties>$(NuspecProperties);version=$(PackageVersion)</NuspecProperties>
      <NuspecProperties>$(NuspecProperties);description=$(Description)</NuspecProperties>
      <NuspecProperties>$(NuspecProperties);authors=$(Authors)</NuspecProperties>
    </PropertyGroup>
  </Target>
</Project>

This will automatically look for a .nuspec file with the same name as the project (somelib.csproj => somelib.nuspec) and pass some properties along to it. The properties are created in a target in order to be able to access fully resolved and defaulted properties like PackageVersion.

The .nuspec file could look like this:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <authors>$authors$</authors>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <dependencies>
      <group targetFramework=".NETStandard1.4">
        <dependency id="NETStandard.Library" version="1.6.1" exclude="Build,Analyzers" />
      </group>
    </dependencies>
  </metadata>
  <files>
    <file src="bin$config$
etstandard1.4*.dll" target="lib
etstandard1.4" />
  </files>
</package>

Note that you must add all referenced NuGet packages as a <dependency> element in the .nuspec file since these are no longer automatically generated from the <PackageReference> items in your project file. Refer to the NuSpec Reference for more details.

I have recently created an example project on GitHub demonstrating the use of a custom .nuspec file for exactly this purpose.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...