乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 面向所有项目风格的Nuget打包发布/离线部署(.NET Framework、.NET Standard)
什么是Nuget
NuGet是Microsoft开发平台的程序集包管理器,它由客户端工具和服务端站点组成,客户端工具提供给用户管理和安装/卸载软件程序包,以及打包和发布程序包到NuGet服务端站点等功能,服务端站点存储已经发行的软件包,并为NuGet客户端软件包库提供服务,使软件包共享给其他的开发者。
适用于任何现代开发平台的基本工具可充当一种机制,通过这种机制,开发人员可以创建、共享和使用有用的代码。通常,此类代码捆绑到“包”中,其中包含编译的代码(如DLL)以及在使用这些包的项目中所需的其他内容。
对于.NET(包括.NET Core),共享代码的Microsoft支持的机制则为NuGet ,其定义如何创建、托管和使用面向.NET的包,并针对每个角色提供适用工具。
简单来说,NuGet 包是具有.nupkg
扩展的单个ZIP文件,此扩展包含编译代码(Dll)、与该代码相关的其他文件以及描述性清单(包含包版本号等信息)。 使用代码的开发人员共享创建包,并将其发布到公用或专用主机。 包使用者从适合的主机获取这些包,将它们添加到项目,然后在其项目代码中调用包的功能。 随后,NuGet自身负责处理所有中间详细信息。
由于NuGet支持公用nuget.org
主机旁边的专用主机,因此,可以使用NuGet包来共享组织或工作组专用的代码。 此外,你还可以使用NuGet包作为一种便捷的方式,将自己的代码用于除你自己项目之外的任何其他项目。 简而言之,NuGet包是可共享的代码单元,但不需要暗示任何特定的共享方式。
识别项目格式
NuGet适用于所有.NET项目。但是,项目格式(SDK样式或非SDK样式)决定了使用和创建NuGet包所需的一些工具和方法。SDK样式的项目使用SDK属性。确定项目类型非常重要,因为用于使用和创建NuGet包的方法和工具都依赖于项目格式。对于非SDK样式的项目,方法和工具还取决于项目是否迁移到
PackageReference
格式。
项目类型 | 默认项目格式 | CLI工具 |
---|---|---|
.NET Standard | SDK样式 | dotnet CLI |
.NET Core | SDK样式 | dotnet CLI |
.NET Framework | 非SDK样式 | nuget.exe CLI |
手动识别
使用Visual Studio Code打开*.csproj
,如果Project
节点,存在属性Sdk
基本上就是SDK样式的。
SDK样式示例
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
非SDK样式示例
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
差异点
在非SDK样式
项目文件夹,我们发现,它会有一个Properties
文件夹,里面有个我们熟知的AssemblyInfo.cs
记录了这个项目的信息。
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("ExampleWinforms")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ExampleWinforms")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("3f148d75-9dd3-4a79-b72d-a530b0bcbc7f")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
而在SDK样式
项目文件夹,并不存在Properties
文件夹,项目信息是直接记录在*.csproj
中的。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Description>测试</Description>
<Copyright>版权说明</Copyright>
<PackageProjectUrl>https://www.baidu.com</PackageProjectUrl>
<AssemblyVersion>1.0.0</AssemblyVersion>
<FileVersion>1.0.0</FileVersion>
<PackageReleaseNotes>发行说明</PackageReleaseNotes>
<RepositoryUrl>git.xxxxxxxxxxxxxxxxxxxxxx</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Title>ExampleWinFormsApp</Title>
<PackageId>ExampleWinFormsApp</PackageId>
<Version>1.0.0</Version>
<Authors>TaylorShi</Authors>
<Company>TaylorShi Inc.</Company>
</PropertyGroup>
</Project>
非SDK样式项目打包
对于较为传统的.Net framework的项目,它属于非SDK样式项目,所以对应的打包工具就是nuget.exe
获取Nuget.exe
NuGet.exe 5.0及更高版本需要
.NET Framework 4.7.2
或更高版本才能执行。
直接从nuget.org
官方下载一个即可。
建议是丢C:\Windows
目录即可,省去配置什么环境变量。
Linux下安装Nuget
sudo apt install mono-runtime
先安装Mono 4.4.2+版本,然后执行如下命令:
sudo curl -o /usr/local/bin/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
编辑~/.bash_aliases
或者~/.bash_profile
,追加如下别名。
sudo vim ~/.bash_aliases
# Create as alias for nuget
alias nuget="mono /usr/local/bin/nuget.exe"
重新进入一次Shell,然后试试nuget ?
来验证安装。
自我更新Nuget.exe
如果你之前已经安装过它,还可以调用它自己的命令来更新它,因为前面我们放到C:\Windows
目录,所以这里需要用管理员运行Windows Terminal。
nuget update -self
准备项目属性
对非SDK样式项目来说,它的项目属性信息是在项目
-属性
-程序集信息
对话框中设置的,对应的文件是.\Properties\AssemblyInfo.cs
把这里面能填的信息都填下,程序集版本和文件版本建议保持一致,等下会作为Nuget包的版本号,前三位就行,最后一位可以是0。
创建Nuget初始清单
在当前这个.csproj
目录,执行如下命令:
nuget spec
它会针对.csproj
这个项目生成一个初始清单,后缀为*.nuspec
。
编辑补充初始清单
接下来我们可以使用Visual Studio Code,它刚开始的内容差不多是这样:
<?xml version="1.0" encoding="utf-8"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>$author$</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<!-- <icon>icon.png</icon> -->
<projectUrl>http://project_url_here_or_delete_this_line/</projectUrl>
<description>$description$</description>
<releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
<copyright>$copyright$</copyright>
<tags>Tag1 Tag2</tags>
</metadata>
</package>
这里面,有些如果有就填,没有的话建议删除掉,比如:
projectUrl
,将包上传到nuget.org
时,字段projectUrl
限制为4000个字符,如果有Github或者官网建议填下。releaseNotes
,将包上传到nuget.org
时,字段releaseNotes
限制为35,000个字符。tags
,这个建议填一填,方便根据关键词搜索到。
接下来,我们可以给它弄个图标,这样好看点。
首先我们需要准备一个图标文件,比如就叫icon.png
,把它放到.csproj
目录,官方建议是128x128的PNG或者JPEG格式,体积小于1MB。
然后我们需要声明这个图标File文件,比如:
<files>
<file src=".\icon.png" target="images\" />
</files>
这里src
就填相对目录就可以了,这个target
其实有点像个命名空间,意味着这个png挂在images\
下面了,所以在<icon>
那里应该写成:
<icon>images\icon.png</icon>
如果你想要Nuget.org上呈现更精彩格式的说明,也可以内置一个markdown进来,先准备一个名为readme.md
的文件,里面先随便写点。
然后以文件形式加进来
<files>
<file src=".\readme.md" target="docs\" />
</files>
以readme
节点添加进来
<readme>docs\readme.md</readme>
最终效果:
<?xml version="1.0" encoding="utf-8"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>$author$</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<icon>images\icon.png</icon>
<readme>docs\readme.md</readme>
<projectUrl>https://www.nuget.org/packages?q=Microsoft.Extensions.Logging</projectUrl>
<description>$description$</description>
<releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
<copyright>$copyright$</copyright>
<tags>Microsoft TestPackage</tags>
</metadata>
<files>
<file src=".\icon.png" target="images\" />
<file src=".\readme.md" target="docs\" />
</files>
</package>
生成Nuget包
在当前这个.csproj
目录,执行如下命令,它就会根据我们刚才.nuspec
和\Properties\AssemblyInfo.cs
的信息结合起来,形成我们要的Nuget包。
nuget pack
最终效果有没有达到预期,我们可以用离线Nuget包源来验证下就知道了,方式后面会介绍。
SDK样式项目打包
获取DotNet CLI
其实这个你装了DotNet Core SDK的时候已经自带了,可以执行命令验证下:
dotnet --version
准备打包信息
创建一个.Net Standard的类库项目,并且添加到解决方案中:
dotnet new classlib --framework "netstandard2.0" -o netStandardLibrary
dotnet sln add .\netStandardLibrary\netStandardLibrary.csproj
对SDK风格的项目来说,一切都简单了,打包信息直接在.csproj
文件中,默认它可以是这样子:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
接下来只需要在PropertyGroup
节点中来添加打包所需的信息就是了。
<PackageId>netStandardLibrary</PackageId>
<Version>1.0.0</Version>
<Authors>Taylor</Authors>
<Company>Taylor Inc.</Company>
甚至还可以添加开关配置,这样它默认就会生成Nuget包
<PropertyGroup>
<PackRelease>true</PackRelease>
</PropertyGroup>
生成Nuget包
同样的,还是在.csproj
目录,直接执行Dotnet CLI的命令即可:
dotnet pack -c Release
这时候在.\bin\Release\netStandardLibrary.1.0.0.nupkg
就得到这个文件了。
发布到Nuget.org
获取Nuget.org密钥
首先你需要准备一个微软账号,然后使用这个账号来授权登录https://www.nuget.org即可,期间会让你选一个名字注册。
然后前往账号菜单下的API Keys
页面。
点击Create
按钮创建一个新的授权密钥
其中Key Name
随便填,但是Glob Pattern
那里可以填*
,代表没有限制,过期时间目前最长也就365
天,然后点击Create
按钮即可。
创建成功之后,就可以在Manage
列表中看到它,这里有一个Copy
按钮,点击它,并且妥善保存好这个内容。
这里特别注意,这个Copy
只有一次机会,点击后就没得点了,所以这个操作前往别犯糊涂。
推送到Nuget.org
这里有个特别要注意的技巧,通过发布多个包的时候,会存在依赖关系,我们应该将被依赖的包先发布,再逐步发布后面的包。
推送到Nuget.org特别简单,一句命令即可:
nuget push .\xxxxxxx.xxxxxxx.xxxxxxx.x.x.x.nupkg $ApiKey -Source https://api.nuget.org/v3/index.json
将其中xxxxxxx.xxxxxxx.xxxxxxx.x.x.x.nupkg
替换成自己包的名称即可,将$ApiKey
替换成前面保管好的密钥。
这里如果你是要推送到自己的私有仓库,只需要把ApiKey和Source改成自己私有仓库对应的即可。
看到Your package was pushed.
即表示发布成功。
这时候前往Nuget官网或者在Visual Studio进入Nuget包管理就可以搜到它了(新包大概需要5-6分钟建立索引)。
离线安装Nuget包
有时候,如果你在本地打包好了,但是不想发布到包平台,其实也可以本地离线安装,这个操作起来非常方便。
新建本地包源
打开Visual Studio顶部菜单中的工具
-Nuget包管理器
-程序包管理器设置
进入Nuget包设置页面。
切换到左侧Nuget包管理器
-程序包源
,在右上角找到+
按钮添加一个新的包源。
修改掉创建后的默认值,比如名称可以改成Local Package Source
,然后点击源
那一行的...
按钮选择放置了本地Nuget包的文件夹,最后点击更新
按钮。
使用本地包源
接下来,前往你要添加Nuget包的项目上右键,进入管理Nuget程序包
菜单,进入浏览
界面,关键的一步是,切换最右侧的包源为刚才添加的本地那个。
这时候你就可以看到你本地的包了,你可以像平时一样安装即可。
不过需要注意的是,如果你安装的包如果对其他包存在依赖,那么你可能需要先安装依赖包,也许这个依赖包是公开包源的,需要先切回如Nuget.org
等在线包源。
Nuget配置
配置文件位置
Nuget的行为由一个或多个Nuget配置文件(nuget.config
)的累积设置驱动。
配置文件可能存在哪种位置:
- 解决方案
在解决方案文件夹根目录或者往上到驱动器目录的任何文件夹中,只要发现了nuget.config
文件就会被累积。
在解决方案层面设置一个nuget.config
可以让我们的nuget配置随着解决方案一起携带,便于团队内部实现私有Nuget服务的开箱即用。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
- 用户级别配置
在用户的目录位置%appdata%\NuGet\
存在NuGet.Config
文件,它将影响当前用户下所有项目的Nuget配置。
- 计算机级别配置
在当前计算机的%ProgramFiles(x86)%\NuGet\Config
存在xxxx.config
文件,它将影响当前计算机下所有项目的Nuget配置。
Microsoft.VisualStudio.Offline.config
内容如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Microsoft Visual Studio Offline Packages" value="C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\"/>
</packageSources>
</configuration>
Microsoft.VisualStudio.FallbackLocation.config
内容如下:
<?xml version="1.0" encoding="utf-8"?>
<!--<auto-generated>
This code was generated by a tool.
Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.
</autogenerated>-->
<configuration>
<fallbackPackageFolders>
<add key="Microsoft Visual Studio Fallback Folder" value="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages" />
</fallbackPackageFolders>
</configuration>
参考
- https://github.com/TaylorShi/HelloNugetPackage
- NuGet简介
- 安装NuGet客户端工具
- 快速入门:使用 Visual Studio 创建和发布包(.NET Framework、Windows)
- 使用dotnet CLI创建NuGet包
- 标识项目格式
- .NET Framework生成NuGet包
- C# NuGet打包和离线安装
- Visual Studio NuGet离线安装
- 共享你的控件 -- 用NuGet包装自己的控件
- .NET Framework生成NuGet包
- https://www.nuget.org
- .NET SDK项目的MSBuild引用
- .nuspec引用
- 快速入门:创建和发布包 (dotnet CLI)
- 常见的NuGet配置