打包專案為 NuGet Package

  • 3010
  • 0
  • 2019-10-03

本篇紀錄如何打包 Library 為 NuGet Package,以 package 形式釋出,引用時可以少許多操作,尤其是一些有相依性的場景。

現在的 .NET 開發者,大概已經習慣 NuGet 一鍵引用的便利性了吧,但在內部共享一些不公開 Library 的時候,仿佛又回到了石器時代

  • 下載(Mail、FTP、雲端空間)
  • 在滿是檔案的下載資料夾找到它
  • Lib 用了其它套件或組件,也要人工補上(沒人問就只能猜了)

需要對檔案進諸多操作,頻繁的視窗切換,最讓人煩躁的一點,就是不免要把 .dll 檔加入版控,不然 code 別人載下來根本編不過。

可以架個私有 NuGet Server,內部發佈 NuGet Package,改善問題。

使用 Visual Studio 打包

雖然官網[1]有寫 Visual Studio 支援 Create Packages,但測試後發現 VS2017 似乎只支援 .NET Core 專案的 NuGet 打包。

可以在專案的屬性頁 -> 套件(Package),勾選 "建置時產生 NuGet 套件"(Generate NuGet package on build)

使用 nuget.exe 打包

nuget.exe 打包靈活度很高,可跨平台,建 CI/CD 也方便寫 script 指令操作。

大概是在 VS2013 到 2015 的某一版,Visual Studio 已內建 NuGet Assembly,雖然可以使用 VS 操作 NuGet 相關功能,但在電腦是找不到 nuget.exe 的。

可到下列網址下載最新版 nuget.exe。

https://dist.nuget.org/win-x86-commandline/latest/nuget.exe

nuget.exe 是一支 CLI 工具[3],其 pack 指令可以根據 .csproj 或 .nuspec 生成 package,例如

NuGetExmaple\NuPack>.\nuget\nuget.exe pack ..\HelloLib\HelloLib.csproj

>後面才是指令,以下為範例檔案結構

+ NuGetExample
  + HelloLib
    - HelloLib.csproj
    - HelloLib.nuspec
  + NuPack
    + nuget
      - download-nuget.ps1
      - nuget.exe
    - pack.ps1
    - HelloLib.1.0.0.nupkg
  - NuGetExample.sln

因為是在 NuPack 執行指令,所以會在 NuPack會產生一個檔名帶版號的 .nupkg 檔。

要注意的是 pack 不會建置 .csproj 且預設打包 bin\Debug,所以在打包前需要先建置專案產生 .dll 檔,不然會打包失敗或打包到舊版。 

此時 package 有許多屬性都是預設的,例如 Authors 會是電腦登入 User、Description 寫著 Description。可以用 NuGet Package Explorer 進行修改,也可以用 .nuspec 配置讓 nuget.exe 在打包時就寫入屬性。

初始的 .nuspec 可在 HelloLib.1.0.0.nupkg 取得,可用 zip 解壓縮,其中包含 HelloLib.nuspec,為 XML 文檔。格式如下

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
  <metadata>
    <id>HelloLib</id>
    <version>1.0.0</version>
    <title></title>
    <authors>Ligi</authors>
    <owners>Ligi</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Description</description>
    <dependencies />
  </metadata>
</package>

將其配置到 HelloLib 和 .csproj 同一層,並修改內容,重新打包即可。

如果想打包 Release,可在指令加上 Properties(不要忘了編譯 Release)

NuGetExmaple\NuPack>.\nuget\nuget.exe pack ..\HelloLib\HelloLib.csproj -Properties Configuration=Release

Script it

如果可以,想儘量保持版控為純文檔,所以將配置下載 nuget.exe 的指令。語法使用 powershell 因為 cmd 要可以下載檔案,需要額外安裝。

(New-Object Net.WebClient).DownloadFile("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe","nuget.exe");

寫在 download-nuget.ps1 內如下,其中 $PSScriptRoot 值為指令檔資料夾路徑,因為我希望下載的檔案會在指令檔旁邊

(New-Object Net.WebClient).DownloadFile("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe","$PSScriptRoot/nuget.exe");

第一次使用 powershell 指令檔,可能會被中止執行,因為 powershell 預設是不執行指令檔的,可以參考PowerShell老是出現已停用指令碼執行的解決辦法

我的配置是 RemoteSigned,可執行本地 script,不可執行遠端未簽署 script,使用 powershell 如下指令進行配置

Set-ExecutionPolicy RemoteSigned

將下載 nuget.exe 和打包整合為指令檔 pack.ps1 如下

param(
    [string] $nuget =  ".\NuGet\nuget.exe"
)

if(!(Test-Path $nuget)){
    .\NuGet\download-nuget.ps1
}

# TODO build project release before pack
& $nuget @("pack", "..\HelloLib\HelloLib.csproj","-Properties", "Configuration=Release")

拿到 sorce code 的人,只要執行  pack.ps1,即可打包出 HelloLib 的 NuGet Package,因為還沒整理出安裝不同版本 VS 都能順利建置專案的 script,所以這裡還是需要在 pack 前先手動建置專案。

NuGet Package Explorer

NuGet Package Explorer 是一個好用、開源的 package 檢視編輯小工具,可以微軟商店裡直接安裝

在做一些比較不熟悉的 .nupec 編寫時,比起直接修改 .nupec 去試,可以用 Explorer 開啟 package,它編輯屬性及相依性的 UI 蠻友善的。

尤其是編輯與其它 package 之間相依性,特別方便,而且 NuGet 會在引用時一併引用相依的 package。

編輯後,另存 package 將其中的 .nuspec 檔取出,參考它修改專案的 .nuspec 就可以在 nuget pack 時寫入相依設定。

後續

NuGet Package 內容大致是由 content, lib, tools 等資料夾和 .nuspec 構成。

當釋出的內容,需要專案加入檔案像 xxx.config 甚至是 xxx.cs 才能使用時,就會用到 content。

當釋出需要適當修改組態檔才能使用,就會用到 tools 在裡面撰寫 install.ps1 和 uninstall.ps1。

lib 的資料夾結構則可以讓 package 同時支援多 framework 包含 .NET, Standard, Core。

最近遇到 content 的撰寫在 .NET 和 Core 的寫法,也有所不同,有點麻煩。

參考

[1]https://docs.microsoft.com/zh-tw/nuget/install-nuget-client-tools#feature-availability

[2]https://docs.microsoft.com/zh-tw/nuget/create-packages/creating-a-package#run-nuget-pack-to-generate-the-nupkg-file

[3]https://docs.microsoft.com/en-us/nuget/reference/nuget-exe-cli-reference

[4]https://dotblogs.com.tw/gelis/2010/10/23/18532