前段時間折騰了一下,總算是把我自己的圖片快取控制元件(https://github.com/h82258652/HN.Controls.ImageEx)釋出到了 nuget 上,目前已經進入一個比較穩定的版本了,基本沒有很嚴重的 bug 了。其實核心程式碼早就寫完了,後期主要都在折騰持續整合以及自動構建(包含製作 nuget 包)。持續整合使用了 appveyor,園子裡也有不少相關的資料,這裡我就不說了。
在製作 nuget 包的過程中,我折騰了很久,最開始打算直接用 appveyor 的自動打包功能,但是在打包 UWP 的包時,打包出來的版本號一直都是 1.0.0,而 WPF 的就沒這問題(這個已確認是 appveyor 的 bug,然而好久都還沒有修複(lll¬ω¬))。另外包裡的 dll 的版本號也不好統一控制,我發一次版本需要發 3 個 nuget 包,每個 nuget 包都有一個 dll,手動折騰版本號那不切實際的。
在經過一番調研之後,我終於發現了一個能滿足我需求的工具,Cake,也叫 C# Make,是一個專門用來進行 .net 專案構建的工具。官方網站在此:https://cakebuild.net/
接下來就按著教程開始吧。
這個是我專案的根目錄。接下來我們在此目錄執行 Powershell(可以透過左上角的檔案 –> 開啟 Windows Powershell 來開啟)。然後輸入以下命令。
Invoke-WebRequest https://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1
然後敲下回車,稍微等待之後,我們的目錄下就會出現一個叫 build.ps1 的檔案。
接下來我們在該目錄建立一個叫 build.cake 的空白檔案,這個檔案主要就是執行構建的邏輯。
然後在宇宙最強的 IDE,Visual Studio 中進行編寫指令碼吧,在這裡,建議各位看官先安裝一個外掛:
安裝之後,Visual Studio 就具備對 .cake 指令碼的語法高亮能力(可惜還是沒法有語法提示功能/(ㄒoㄒ)/~~)。
使用 Visual Studio 開啟 build.cake 之後,先編寫個 Hello world 熱熱身:
var target = Argument("target", "Default"); Task("Default") .Does(() => { Information("Hello World!"); }); RunTarget(target);
然後儲存並執行剛才的 build.ps1。(Powershell 裡輸入 .\build.ps1)
如果運氣好的話,那麼你應該和我一樣碰到了這樣的情況:
這是由於執行 Powershell 指令碼是一直比較危險的操作,所以需要更改許可權。
輸入如下的指令碼:
或者可以參考微軟的官方檔案來修改許可權:https://docs.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-6
接著再次執行指令碼,應該就可以看見如下資訊。
我們的 Hello world 終於跑起來了,熱身完畢,接下來開始編寫構建指令碼。
Cake 指令碼是由一個個 Task 串聯起來的,我們先定義一些變數和一個叫 Build 的 Task,用於執行構建。
var target = Argument("target", "Default"); var configuration = Argument("configuration", "Release"); var verbosity = Argument("verbosity", Verbosity.Minimal); var solution = "./HN.Controls.ImageEx.sln"; Task("Build") .Does(() => { if(IsRunningOnWindows()) { // Use MSBuild MSBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } else { // Use XBuild XBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } }); Task("Default") .IsDependentOn("Build"); RunTarget(target);
這裡定義了一些變數,configuration 定義為 Release,在 Build Task 裡用到,設定為使用 Release 樣式。verbosity 表示編譯時的資訊輸入,這裡設定為 Minimal,以免輸出過多的資訊。solution 表示解決方案的路徑。然後執行:
這樣一執行就把這個解決方案構建了一遍。
然後我們開始編寫打包的 Task,命名為 Package。修改指令碼如下:
var target = Argument("target", "Default"); var configuration = Argument("configuration", "Release"); var verbosity = Argument("verbosity", Verbosity.Minimal); var version = Argument("version", "1.0.0"); var solution = "./HN.Controls.ImageEx.sln"; Task("Build") .Does(() => { if(IsRunningOnWindows()) { // Use MSBuild MSBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } else { // Use XBuild XBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } }); Task("Package") .IsDependentOn("Build") .Does(() => { var nuGetPackSettings = new NuGetPackSettings { Version = version }; var nuspecFiles = GetFiles("./src/*/*.nuspec"); NuGetPack(nuspecFiles, nuGetPackSettings); }); Task("Default") .IsDependentOn("Package"); RunTarget(target);
在這裡我加了一個 version 的變數,在下麵打包 nuget 包的時候會用到,統一每個 nuget 包的版本號。GetFiles(“./src/*/*.nuspec”) 這個則獲取了源檔案夾下麵的專案下的 nuspec 檔案(我這裡一個 nuspec 對應一個 csproj,就放到同一個檔案夾下了),這個檔案的作用是用於描述 nuget 包如何進行打包,具體可以參考本文開頭指向的上一篇文章。
執行之後,我們會得到些 nuget 包(當然對於看官你們的專案需要有 nuspec 才行啦):
編譯、打包都說完了。最後就是版本號的問題。nuget 包的版本在上面已經解決了,現在就是 dll 的版本號比較棘手。在我這三個專案中,WPF 和 UWP 都是傳統的專案,都是有一個 AssemblyInfo.cs 的檔案,然後裡面透過 AssemblyVersionAttribute 來設定 dll 的版本號的。但是我這個 Core 的專案是新型別的專案,並沒有 AssemblyInfo.cs 檔案,版本號是在 csproj 裡設定的。這難道沒辦法了麼,最後透過萬能的 Google 和 StackOverflow,我還是找到了辦法。編輯 csproj 檔案,並新增下麵一節。
這樣,這個專案的版本號等資訊就不會從 csproj 裡面讀取。我們可以新增自己的 AssemblyInfo.cs 檔案進行版本號管理。
現在情況就是每個專案都有自己的 AssemblyInfo.cs 了,如何統一使用一個 AssemblyInfo.cs 檔案來管理這幾個專案呢?還記得 Visual Studio 有一個新增取用檔案的功能麼?
這樣幾個專案都透過這種方式取用同一個 AssemblyInfo.cs 檔案就行了。
最後的問題就是如何將 AssemblyInfo.cs 裡的版本號跟 build.cake 腳本里的版本號保持一致。在查閱 Cake 的官方檔案後,我發現有一個能生成 AssemblyInfo 的功能。修改我們的 build.cake 指令碼:
var target = Argument(“target“, “Default“);
var configuration = Argument("configuration", "Release"); var verbosity = Argument("verbosity", Verbosity.Minimal); var version = Argument("version", "1.0.0"); var solution = "./HN.Controls.ImageEx.sln"; Task("Version") .Does(() => { var file = "./src/SolutionInfo.cs"; CreateAssemblyInfo(file, new AssemblyInfoSettings { Version = version, FileVersion = version, InformationalVersion = version }); }); Task("Build") .IsDependentOn("Version") .Does(() => { if(IsRunningOnWindows()) { // Use MSBuild MSBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } else { // Use XBuild XBuild(solution, configurator => configurator.SetConfiguration(configuration) .SetVerbosity(verbosity)); } }); Task("Package") .IsDependentOn("Build") .Does(() => { var nuGetPackSettings = new NuGetPackSettings { Version = version }; var nuspecFiles = GetFiles("./src/*/*.nuspec"); NuGetPack(nuspecFiles, nuGetPackSettings); }); Task("Default") .IsDependentOn("Package"); RunTarget(target);
在 Version Task 中,會生成一個 SolutionInfo.cs 的檔案,也就相當於前面的 AssemblyInfo.cs。那現在所有的專案都取用這個檔案來進行統一的版本管理就行了。
這樣就已經實現了統一版本、構建、打包的一件指令碼化了。後續還可以新增上構建完之後執行單元測試以及打包後自動釋出到 nuget 的功能,但我自己比較習慣打包完之後本地也測試一下(畢竟 nuget 發了就不能刪),所以就暫時不折騰這功能了。
博主圖片快取控制元件專案的 build.cake 指令碼可以參考這裡:https://github.com/h82258652/HN.Controls.ImageEx/blob/master/build.cake,雖然配置好了 xunit,但是沒有寫單元測試就是了,ε=ε=ε=┏(゜ロ゜;)┛
這篇博文主要記錄操作步驟,方便以後自己(我還有個微博的庫的坑想要填……)。但也希望看完這篇博文的各位,製作一個 nuget 包並不是一件難事,馬上行動把珍藏都弄一份 nuget 包,讓各位 .net 開發者也機會用上吧。
原文地址:https://www.cnblogs.com/h82258652/p/10625036.html