使用 dotnet 命令列工具發行 .NET 6 專案
5 |
.NET 5 時介紹過用 Visual Studio 發行 .NET 5 專案,.NET 6 這篇改整理 .NET CLI (dotnet 命令列工具) 發行技巧。
主要參考來源為微軟文件 - .NET application publishing overview
dotnet publish 產生的執行程式有兩種形式:可執行檔 (在 Windows 為 .exe,在 Linux/macOS 則是與專案同名的無附檔名檔案) 或跨平台程式檔 (.dll,用 dotnet appName.dll 執行)。
可執行檔種類取決執行 dotnet publish 所在的平台,加上 -r win-x64、-r linux-x64 則可產生特定平台用的可執行檔,win-x64 是所謂 RID ( .NET 6 支援的 RID 清單),不同平台又再有 Self-Contained 及 Framework-Dependent 兩種選擇。
- Self-Contained
部署結果包含 .NET Runtime,部署對象不需先安裝 .NET SDK 或 .NET Runtime,缺點是檔案較多,且每支程式重複自帶 Runtime 會多佔用磁碟空間 - Framework-Dependent
只包含程式及其參照程式庫,部署對象必須裝好 .NET SDK 或 .NET Runtime
由可執行檔 vs 跨平台程式檔,Self-Contained vs Framework-Dependent 兩個維度排列組合,dotnet publish 參數分別為:(適用 .NET Core 3.1、.NET 5/6,排除 .NET 2.1)
- Framework-Dependent 跨平台程式檔
dotnet publish -c Release -p:UseAppHost=false - Framework-Dependent 可執行檔
dotnet publish -c Release -r <RID> --no-self-contained
dotnet publish -c Release -r <RID> --self-contained false
dotnet publish -c Release (未指定 -r 時,產出目前所在平台的可執行檔) - Self-Contained 可執行檔
dotnet publish -c Release -r <RID> --self-contained - Self-Contained 跨平台程式檔
無此組合
先列出主要可用參數,後面再補充說明
- PublishSingleFile
- UseAppHost
- IncludeNativeLibrariesForSelfExtract
- IncludeAllContentForSelfExtract
- IncludeSymbolsInSingleFile
- PublishReadyToRun
- EnableCompressionInSingleFile
以上參數有兩種設定方式,直接寫在 .csproj:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>dotnet_publish_test</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishSingleFile>false</PublishSingleFile>
</PropertyGroup>
或是 dotnet publish 加上 -p:PublishSingleFile=true 設定,它將覆寫 .csproj 裡的設定:
dotnet publish -c Release -r win-x64 --no-self-contained -p:PublishSingleFile=true
簡單介紹各參數用法。
如果覺得 Publish 結果包含一堆 DLL 太亂,有個 PublishSingleFile 參數可將所有 DLL 整併成單一執行檔,部署時一檔搞定。但 --self-contained 時會內含 .NET Runtime,執行檔大小常超過 70MB,有個 PublishTrimmed 參數可排除沒用到 DLL,執行檔有時可瘦身一半以上。但偵測未用 DLL 很難完全精準,瘦身版務必仔細測試再上線。
PublishSingleFile 預設只會整併 .NET 寫的 DLL (Managed DLL),DLL 依附的原生函式庫仍保留獨立檔案形式,IncludeNativeLibrariesForSelfExtract 參數能將這部分也合併進執行檔,執行時原生 DLL 檔將匯出到特定目錄(在 Windows 為 %TEMP%/.net、Linux 及 MacOS 為 $HOIME/.net,或可使用 DOTNET_BUNDLE_EXTRACT_BASE_DIR 環境變數指定)。依實測,沒用到原生 DLL,PublishSingleFile 併入的 Managed DLL 可直接載入記憶體使用,檔案不需落地;但如用到 IncludeNativeLibrariesForSelfExtract 併入原生 DLL,則需一併指定 IncludeAllContentForSelfExtract,將原生 DLL 跟參照 .NET DLL 都匯出到 %TEMP%/.net、$HOME/.net 或 DOTNET_BUNDLE_EXTRACT_BASE_DIR 指定目錄,才能正確載入原生 DLL。實地觀察可在 .net 目錄看到匯出檔案:
PublishSingleFile 合併成單一檔案後,仍會有 .pdb 檔,使用 IncludeSymbolsInSingleFile 則可合併 .pdb 內容,實現真正單一檔案。
若有特殊 DLL 檔不要併入執行檔但要放進發行目錄,可修改 csproj:
<ItemGroup>
<Content Update="Plugin.dll">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
另外,PublishReadyToRun 參數可指定預先完成 JIT 編譯,程式碼體積會變大,但可節省程式啟動時間。
.NET 6 起,多了 EnableCompressionInSingleFile 參數可壓縮併入執行檔的 DLL,進一步縮小體積。但程式啟動時需先在記憶體中解壓縮,要付出效能上的代價,建議視應用情境取捨。
[2021-12-14 補充] dotnet publish 預設包含編譯程序,還有個 --no-build 參數可直接使用 obj 下的內容。若發行程序有使用混淆器處理 obj 目錄檔案,--no-build 可避免混淆過的檔案被覆寫,但此時不可用 PublishTrimmed。(感謝讀者 Peter Cheng 分享)
最後來個範例整理:
- dotnet publish -c Release
不含 .NET Runtime,同時產出 .dll 及 .exe (或是所在平台的可執行檔),部署對象需安裝 .NET Runtime/SDK - dotnet publish -c Release -p:UseAppHost=false
不含 .NET Runtime,只產出 .dll,部署對象需安裝 .NET Runtime/SDK,使用 dotnet appName.dll 執行 - dotnet publish -c Release -r win-x64 --self-contained
包含 Windows x64 .NET Runtime 以及 .exe 可執行檔 - dotnet publish -c Release -r linux-x64 --no-self-contained
產生 64 位元 Linux 用的 .exe 可執行檔,部署對象需安裝 .NET Runtime/SDK - dotnet publish -c Release -r linux-x64 --self-contained
包含 64 位元 Linux .NET Runtime 以及 Linux 用的可執行檔 - dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true
將 Windows x64 .NET Runtime、參照的 .NET .dll,以及程式本身打包成一個 .exe 可執行檔 (大小可能超過 70MB),但參照程式庫附屬的原生程式庫仍維持個別檔案 - dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:IncludeAllContentForSelfExtract=true 同 6,但將參照程式庫附屬的原生程式庫也包進 .exe,實測 IncludeNativeLibrariesForSelfExtract 需配合 IncludeAllContentForSelfExtract 參數才能正確執行
- dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=true
同 6,但進行程式瘦身,剔除沒用到的程式庫部分,.exe 甚至可縮小一半以上。但有可能出現誤刪,部署前請慎重測試
Summary of using .NET CLI to publish .NET 6 project.
Comments
# by 阿光
黑暗大哥你好, 如果我要同時publish到多台網站主機,而且我只要把bin更新就好,其他web.config、app.config都不要發布,要怎麼寫成自動化指令呢?
# by 阿光
忘了說,我的環境是.net 4.7.2,windows 2012 R2
# by Jeffrey
to 阿光,這篇介紹的是 .NET 6,與 .NET Framework (4.0~4.8) 發佈或複製檔案到主機關係不大。但你說的複製檔案到多台主機的需求我也有遇到,以前是用輔助工具產生指令腳本 https://blog.darkthread.net/blog/copy-script-generator/ ,現在的話應該會用 PowerShell 寫 for 迴圈處理,這裡有更複雜的範例 https://blog.darkthread.net/blog/sync-files-by-hash/
# by 阿光
真是抱歉發錯地方,謝謝黑暗大哥的提醒。 也謝謝你的建議,我會去拜讀您的文章研究看看。
# by Will
IncludeSymbolsInSingleFile 從 .NET 5 開始就不能用了,要改用 -p:DebugType=embedded