記錄使用 Cake 進行構建並製做 nuget 包

書接上一回(http://www.javashuo.com/article/p-tnurzexd-cd.html)?[手動狗頭]html

前段時間折騰了一下,總算是把我本身的圖片緩存控件(https://github.com/h82258652/HN.Controls.ImageEx)發佈到了 nuget 上,目前已經進入一個比較穩定的版本了,基本沒有很嚴重的 bug 了。其實核心代碼早就寫完了,後期主要都在折騰持續集成以及自動構建(包含製做 nuget 包)。持續集成使用了 appveyor,園子裏也有很多相關的資料,這裏我就不說了。git

在製做 nuget 包的過程當中,我折騰了好久,最開始打算直接用 appveyor 的自動打包功能,可是在打包 UWP 的包時,打包出來的版本號一直都是 1.0.0,而 WPF 的就沒這問題(這個已確認是 appveyor 的 bug,然而很久都尚未修復(lll¬ω¬))。另外包裏的 dll 的版本號也很差統一控制,我發一次版本須要發 3 個 nuget 包,每一個 nuget 包都有一個 dll,手動折騰版本號那不切實際的。github

在通過一番調研以後,我終於發現了一個能知足我需求的工具,Cake,也叫 C# Make,是一個專門用來進行 .net 項目構建的工具。官方網站在此:https://cakebuild.net/shell

接下來就按着教程開始吧。bootstrap

Snipaste_2019-03-29_23-00-45

這個是我項目的根目錄。接下來咱們在此目錄運行 Powershell(能夠經過左上角的文件 –> 打開 Windows Powershell 來打開)。而後輸入如下命令。windows

Invoke-WebRequest https://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1

而後敲下回車,稍微等待以後,咱們的目錄下就會出現一個叫 build.ps1 的文件。緩存

Snipaste_2019-03-29_23-07-34

接下來咱們在該目錄建立一個叫 build.cake 的空白文件,這個文件主要就是執行構建的邏輯。app

而後在宇宙最強的 IDE,Visual Studio 中進行編寫腳本吧,在這裏,建議各位看官先安裝一個插件:工具

Snipaste_2019-03-29_23-13-08

安裝以後,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

若是運氣好的話,那麼你應該和我同樣碰到了這樣的狀況:

Snipaste_2019-03-29_23-23-33

這是因爲執行 Powershell 腳本是一直比較危險的操做,因此須要更改權限。

輸入以下的腳本:

Snipaste_2019-03-29_23-26-39

或者能夠參考微軟的官方文檔來修改權限:https://docs.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-6

接着再次執行腳本,應該就能夠看見以下信息。

Snipaste_2019-03-29_23-29-22

咱們的 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 表示解決方案的路徑。而後運行:

Snipaste_2019-03-29_23-53-38

這樣一執行就把這個解決方案構建了一遍。

而後咱們開始編寫打包的 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 才行啦):

Snipaste_2019-03-30_00-06-51

編譯、打包都說完了。最後就是版本號的問題。nuget 包的版本在上面已經解決了,如今就是 dll 的版本號比較棘手。在我這三個項目中,WPF 和 UWP 都是傳統的項目,都是有一個 AssemblyInfo.cs 的文件,而後裏面經過 AssemblyVersionAttribute 來設置 dll 的版本號的。可是我這個 Core 的項目是新類型的項目,並無 AssemblyInfo.cs 文件,版本號是在 csproj 裏設置的。這難道沒辦法了麼,最後經過萬能的 Google 和 StackOverflow,我仍是找到了辦法。編輯 csproj 文件,並添加下面一節。

Snipaste_2019-03-30_00-19-52

這樣,這個項目的版本號等信息就不會從 csproj 裏面讀取。咱們能夠添加本身的 AssemblyInfo.cs 文件進行版本號管理。

如今狀況就是每一個項目都有本身的 AssemblyInfo.cs 了,如何統一使用一個 AssemblyInfo.cs 文件來管理這幾個項目呢?還記得 Visual Studio 有一個添加引用文件的功能麼?

Snipaste_2019-03-30_00-33-34

這樣幾個項目都經過這種方式引用同一個 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 開發者也機會用上吧。

相關文章
相關標籤/搜索