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文件。比如下面这个
<?xml version="1.0" encoding="utf-8"?><package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"><metadata><id>#包名#</id><version>#版本号#</version><title></title><authors></authors><owners></owners><requireLicenseAcceptance>false</requireLicenseAcceptance><license type="expression">GPL-3.0-or-later</license><projectUrl></projectUrl><description>#包的描述#</description><summary></summary><releaseNotes>#这个版本的描述#</releaseNotes><copyright>Copyright 2020</copyright><tags>#标签,用空格分隔#</tags><repository type="git" url="<git repository>" branch="master|dev"/></metadata><files><file src="" target="" /></files></package>
metadata
metadata这一簇标签描述了整个包的基本信息,通常用于显示在NuGet页面上。
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
Android
Apple
Windows
.props & .targets
将文件按照RIDs放好之后并不是整个过程的终点。如果我们要在另一个项目中使用这个NuGet包,比如另一个C++项目,就必须给这个项目.h文件的位置;在link的时候,怎么找到对应的lib文件,等等。这些我们就需要用到.targets和.props文件进行指定了。
.props和.targets的区别
| .props | .targets |
|---|---|
| 在大多数情况下,.props文件会较早加载 | 在大多数情况下,.targets文件会较晚加载 |
| 一般把默认值设在.props文件中 | 将依赖于当前项目的属性放在.targets文件中。 如果要覆盖当前项目的一些属性,一定要放在.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)
<a name="A5zzp"></a>### .targets文件示例```xml<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><ItemDefinitionGroup><Link><AdditionalDependencies Condition="'$(Platform)' == 'x64'">$(MSBuildThisFileDirectory)lib\win\x64\sample.lib;%(AdditionalDependencies)</AdditionalDependencies><AdditionalDependencies Condition="'$(Platform)' != 'x64'">$(MSBuildThisFileDirectory)lib\win\x86\sample.lib;%(AdditionalDependencies)</AdditionalDependencies><AdditionalLibraryDirectories Condition="'$(Platform)' == 'x64'">$(MSBuildThisFileDirectory)lib\win\x64\v120;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories><AdditionalLibraryDirectories Condition="'$(Platform)' != 'x64'">$(MSBuildThisFileDirectory)lib\win\x86\v120;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories></Link><ClCompile><AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories></ClCompile></ItemDefinitionGroup><ItemGroup Condition="'$(Platform)' == 'x64'"><ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x64\native\*.dll" /></ItemGroup><ItemGroup Condition="'$(Platform)' != 'x64'"><ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x86\native\*.dll" /></ItemGroup></Project>
用NuGet命令行打包
由于所有文件都不是Visual Studio的项目文件,所以没有办法直接用Visual Studio来进行打包,只能借助于NuGet.exe来进行打包。
#打包文件nuget pack <nuget path> -Version <version>
- 用-Version设定NuGet包的版本
#发布文件nuget publish <nuget package> [-src|-Source <url>]
- -src|-Source 可以指定NuGet的服务位置。如果缺省则上传到nuget.org
参考资料
- NuGet CLI Reference - https://docs.microsoft.com/en-us/nuget/reference/nuget-exe-cli-reference
- Create the .nuspec file - https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#create-the-nuspec-file
- .nuspec reference - https://docs.microsoft.com/en-us/nuget/reference/nuspec
- Support multiple .NET versions - https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks
- .NET Core RID Catalog - https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
- runtime.json - https://github.com/dotnet/runtime/blob/master/src/libraries/pkg/Microsoft.NETCore.Platforms/runtime.json
- Difference between Directory.Build.props and .targets in not clear - https://github.com/MicrosoftDocs/visualstudio-docs/issues/2774#issuecomment-489685855
