MSBuild单个NuGet包中的多个dll

Dis*_*ile 8 msbuild visual-studio-2017

我有一个包含两个项目的Visual Studio 2017解决方案:

Foo.csproj
Foo.Core.csproj
Run Code Online (Sandbox Code Playgroud)

这两个项目都针对多个框架: net452;netstandard1.2

Foo.csproj包含对Foo.Core.csproj的项目引用:

<ItemGroup>
    <ProjectReference Include="..\Foo.Core\Foo.Core.csproj" />
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)

当我为Foo.csproj生成NuGet包时,我希望nupkg文件包含这两个程序集.

目前发生的是创建的NuGet包具有Foo.dll,然后是Foo.Core上的NuGet依赖(它不存在).

如何使用msbuild包含两个程序集的单个NuGet包生成?

作为参考,这是我目前正在使用的命令(这不符合我的要求):

msbuild /p:restore,pack Foo.csproj
Run Code Online (Sandbox Code Playgroud)

Mar*_*ich 20

目前NuGet没有直接支持这个功能.您可以按照此GitHub问题进行更新.

但是,有几种方法可以创建这样的NuGet包.

  1. 使用"Nugetizer 3000"

这是一个新开发的工具,用于从项目构建NuGet包,并通过安装NuGet.Build.Packagingnuget包来工作.你可以在它的GitHub wiki页面上找到一些关于它的文档,但由于它是一个非常新的项目,它周围没有太多的文档或社区知识(!)(但开发它的团队非常有帮助,你可以提交GitHub问题,如果你卡住了).

  1. 在项目中添加自定义目标(2.0.0 tooling/VS 2017 15.3+):在csproj中创建一个项目,该项目将包含引用项目的输出DLL

这种方法非常糟糕,因为它依赖于包目标使用的内部MSBuild项.它的工作原理是首先标记<ProjectReference>不要从创建的nuget包中引用,如下所示:

<ProjectReference Include="..\libA\libA.csproj" PrivateAssets="All"/>
Run Code Online (Sandbox Code Playgroud)

然后,您可以将其添加到项目中以包含libA.dll在nuget包中生成的内容:

<PropertyGroup>
  <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeP2PAssets</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="IncludeP2PAssets">
  <ItemGroup>
    <BuildOutputInPackage Include="$(OutputPath)\testprivatelib.dll" />
  </ItemGroup>
</Target>
Run Code Online (Sandbox Code Playgroud)

请注意,这需要您将<PackageReference>引用项目的所有项目添加到生成包的项目中,因为生成的包中将丢失它们,因为您已经有效地禁用了传递引用行为.

  1. 创建自定义.nuspec文件

在撰写本文时,这可能是最"支持"的方式,但也是最复杂的方式.NuGet允许您.nuspec通过<NuspecFile>在项目中设置属性来禁用自动生成结果文件和自动文件收集,以及<NuspecProperties>允许您传递替换标记以解析.nuspec文件的属性.

这通过修改项目文件来工作,如下所示:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.4</TargetFramework>
    <NuspecFile>$(MSBuildThisFileDirectory)$(MSBuildProjectName).nuspec</NuspecFile>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\LibB\LibB.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>
Run Code Online (Sandbox Code Playgroud)

这将自动查找.nuspec与项目(somelib.csproj=> somelib.nuspec)同名的文件,并将一些属性传递给它.属性在目标中创建,以便能够访问完全解析和默认的属性,如PackageVersion.

.nuspec文件可能如下所示:

<?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$\netstandard1.4\*.dll" target="lib\netstandard1.4\" />
  </files>
</package>
Run Code Online (Sandbox Code Playgroud)

请注意,必须将所有引用的NuGet包添加为文件中的<dependency>元素,.nuspec因为不再从<PackageReference>项目文件中的项自动生成这些包.有关更多详细信息,请参阅NuSpec参考.

我最近在GitHub上.nuspec创建了一个示例项目,演示了如何使用自定义文件.


Bor*_*ode 6

The second option that Martin Ullrich mentioned is the only one that works out of the box with .NET Standard that allows to "Generate NuGet package on build" as an integral part of the build.

However like he mentions it has a "hard coded" dependency on a dll with an exact name that you expect to be there (on the output folder) which might bite you in the future. I've found a better alternative which worked for me in .NET Standard without the need of any other modification on this post.

I'll quote it here for completeness. First you edit your csproj and define the PrivateAssets tag for the reference that you'd like to include:

<ItemGroup>
  <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj">
    <PrivateAssets>all</PrivateAssets>
  </ProjectReference>
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)

Then you add this to your csproj:

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

<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
    <ItemGroup>
        <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'all'))" />
    </ItemGroup>
</Target>
Run Code Online (Sandbox Code Playgroud)

That post also shows how to include the PDBs in the NuGet package option if necessary (which I omitted here).