NuGet是一个开发包管理的工具,是为微软开发平台提供各种工具和服务,就好比Maven之于Java,NPM之于nodeJS。一般的.net Library项目,都可以很方便地打包成一个支持跨平台调用的NuGet包,但是NuGet对于C++ Native的项目就不是很友好了。也许是因为,对于C++ Native来说cross compile实在是太复杂了,无法在一个平台上完成,所以就需要手工来配置打包。

准备工作

首先要有NuGet.exe,可以到官方下载(https://www.nuget.org/downloads)。

Nuspec文件

nuspec是NuGet包的内容的一个配置的XML文件。比如下面这个

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
  3. <metadata>
  4. <id>#包名#</id>
  5. <version>#版本号#</version>
  6. <title></title>
  7. <authors></authors>
  8. <owners></owners>
  9. <requireLicenseAcceptance>false</requireLicenseAcceptance>
  10. <license type="expression">GPL-3.0-or-later</license>
  11. <projectUrl></projectUrl>
  12. <description>#包的描述#</description>
  13. <summary></summary>
  14. <releaseNotes>#这个版本的描述#</releaseNotes>
  15. <copyright>Copyright 2020</copyright>
  16. <tags>#标签,用空格分隔#</tags>
  17. <repository type="git" url="<git repository>" branch="master|dev"/>
  18. </metadata>
  19. <files>
  20. <file src="" target="" />
  21. </files>
  22. </package>

metadata

metadata这一簇标签描述了整个包的基本信息,通常用于显示在NuGet页面上。
2020-09-13_20-32-57.png

files

files标签是定义了怎么将那些编译出来的文件打包到NuGet包中。src属性是源文件的位置,可以使用来表示任意目录,*表示任意字符。例如:rutimes\*.so表示runtimes目录下,所有的.so文件,包括子目录下的。target属性表示将文件复制到包内目录位置。

NuGet包有严格的文件目录规范。

目录 描述 安装方式
根目录 一般放README.txt, LICENSES.txt这类文件 Visual Studio安装包的时候显示
lib/{目标环境} 在不同的目标环境上的.dll, .xml, .pdb等文件。这个目录只放.net的包,不能放native dll。
1. .dll文件会被作为编译或在运行时进行引用
1. .xml, .pdb文件会被copy到Project的目录下
ref/{目标环境} 在不同的目标环境上的.dll, .xml, .pdb等文件。这个目录只放.net的包,不能放native dll。如果ref目录存在,lib目录下的dll在编译时不会被引用。 只在编译的时候会引用的文件
runtimes 在不同的目标环境上的.dll, .xml, .pdb等文件。Native Library放在这个目录下。如果runtime目录存在,在发布的时候,lib目录下的dll在运行时不会被使用。
1. .dll只在运行的时候引用
1. 其他文件会复制到Project目录下
content 其他文件 会被复制到Project目录下。一般情况下是一些包会引用的静态文件或者配置文件
build (Nuget 3.0+)MSBuild的.targets和.props文件 自动插入到Project文件中
buildMultiTargeting (Nuget 4.0+)各个不同平台的MSBuild的.targets和.props文件 自动插入到Project文件中
buildTransitive (Nuget 5.0+)在最终的项目中使用的library使用的.targets和.props文件。具体可以看https://github.com/NuGet/Home/wiki/Allow-package—authors-to-define-build-assets-transitive-behavior 自动插入到Project文件中
tools Powershell脚本,用户可以在Package Manager Console中调用 将tools目录加入Package Manager Console的PATH环境变量。只能Package Manager Console调用。

用NuGet打包C++ Native Library

根据nuspec文件中的描述,那么Native Library的dll或者so文件就必须放在runtimes目录下。.net framework library的目录结构:{rids}\lib{framework};native代码的目录结构:{rids}\native。

Runtime Identifiers (RIDs)

其中,RIDs(Runtime Identifiers)由[操作系统].[版本]-[CPU架构]-[其他标签]组成。其中[版本]是一个可选的。如果不带,则表示所有此类的操作系统上。如果有版本,则表示只对于特别版本起效果。在运行的时候,会选择与当前运行系统最近的RID加载。RID的关系如下图所示

Linux

用NuGet发布C   Native Library - 图2

Android

用NuGet发布C   Native Library - 图3

Apple

用NuGet发布C   Native Library - 图4

Windows

用NuGet发布C   Native Library - 图5

.props & .targets

将文件按照RIDs放好之后并不是整个过程的终点。如果我们要在另一个项目中使用这个NuGet包,比如另一个C++项目,就必须给这个项目.h文件的位置;在link的时候,怎么找到对应的lib文件,等等。这些我们就需要用到.targets和.props文件进行指定了。

.props和.targets的区别

.props .targets
在大多数情况下,.props文件会较早加载 在大多数情况下,.targets文件会较晚加载
一般把默认值设在.props文件中 将依赖于当前项目的属性放在.targets文件中。
如果要覆盖当前项目的一些属性,一定要放在.targets文件中
放在.props文件中 放在.targets文件中

注意:尽量不要用.targets文件来覆盖一些属性。这样会导致一些无法预知的问题。毕竟,微软没有在Visual Studio的文档中注明各个配置文件加载的顺序,反而强调各个文件是无序地加载。

在了解了.props和.targets这两个文件的作用之后,我们可以这样设置.props和.targets文件。

  • .props文件:定义dll的输出目录,将include文件加入到
  • .targets文件:定义Link的时候使用的lib文件;根据不同的环境引用不同的dll。

    .prop文件示例

    ```xml <?xml version=”1.0” encoding=”utf-8” ?> $path$\x86\%(Filename)%(Extension) PreserveNewest $path$\x64\%(Filename)%(Extension) PreserveNewest $(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories)
  1. <a name="A5zzp"></a>
  2. ### .targets文件示例
  3. ```xml
  4. <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  5. <ItemDefinitionGroup>
  6. <Link>
  7. <AdditionalDependencies Condition="'$(Platform)' == 'x64'">$(MSBuildThisFileDirectory)lib\win\x64\sample.lib;%(AdditionalDependencies)</AdditionalDependencies>
  8. <AdditionalDependencies Condition="'$(Platform)' != 'x64'">$(MSBuildThisFileDirectory)lib\win\x86\sample.lib;%(AdditionalDependencies)</AdditionalDependencies>
  9. <AdditionalLibraryDirectories Condition="'$(Platform)' == 'x64'">$(MSBuildThisFileDirectory)lib\win\x64\v120;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
  10. <AdditionalLibraryDirectories Condition="'$(Platform)' != 'x64'">$(MSBuildThisFileDirectory)lib\win\x86\v120;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
  11. </Link>
  12. <ClCompile>
  13. <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
  14. </ClCompile>
  15. </ItemDefinitionGroup>
  16. <ItemGroup Condition="'$(Platform)' == 'x64'">
  17. <ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x64\native\*.dll" />
  18. </ItemGroup>
  19. <ItemGroup Condition="'$(Platform)' != 'x64'">
  20. <ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x86\native\*.dll" />
  21. </ItemGroup>
  22. </Project>

用NuGet命令行打包

由于所有文件都不是Visual Studio的项目文件,所以没有办法直接用Visual Studio来进行打包,只能借助于NuGet.exe来进行打包。

  1. #打包文件
  2. nuget pack <nuget path> -Version <version>
  • 用-Version设定NuGet包的版本
  1. #发布文件
  2. nuget publish <nuget package> [-src|-Source <url>]
  • -src|-Source 可以指定NuGet的服务位置。如果缺省则上传到nuget.org

参考资料

  1. NuGet CLI Reference - https://docs.microsoft.com/en-us/nuget/reference/nuget-exe-cli-reference
  2. Create the .nuspec file - https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#create-the-nuspec-file
  3. .nuspec reference - https://docs.microsoft.com/en-us/nuget/reference/nuspec
  4. Support multiple .NET versions - https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks
  5. .NET Core RID Catalog - https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
  6. runtime.json - https://github.com/dotnet/runtime/blob/master/src/libraries/pkg/Microsoft.NETCore.Platforms/runtime.json
  7. Difference between Directory.Build.props and .targets in not clear - https://github.com/MicrosoftDocs/visualstudio-docs/issues/2774#issuecomment-489685855