2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

Organization

NUKE Build ノススメ

.NET Build tool あれこれ

前振りが面倒な人はNUKE の使い方からどうぞ!

.NET は昔からビルドツールをあまり必要としていません。実際には MSBuild を暗黙的に使っているのですが、意識して使うことはまれなのではないでしょうか?

自分は CI を多用するようになってからビルドツールを利用するようになりました。

  • テストの前にテスト用のデータベースを作成したうえでマイグレーションをする
  • コード検査の結果を成型する
  • リリースタグに合わせてバージョン番号を埋め込む

などなど。

最初のうちはコマンドを並べていただけだったのですが、コミットしてプッシュして、CI ジョブはいまた失敗! を繰り返すうちにいやになってきました。

コマンドがたくさんあると手元でテストするのも大変です。手元でテストしたコマンドを CI に設定するときにコピペミスなんかやってすごくいらいらします。

あれこれ

シェルスクリプト

個人的にはこれが一番好みです。コマンドを並べるのの延長線上ですし。

ですが、開発は Windows で、CI は Linux でとなっているとやりにくいんですね。

  • Windows だと、書いたシェルスクリプトがそう簡単に試せない
  • Windows で Git リポジトリにシェルスクリプトを +x パーミッションどうやって与えるの?
  • 複雑な処理になると、やっぱしんどい

PowerShell

開発が Windows で CI も Windows の時はシェルスクリプトが使えないので、PowerShell を使いました。

.NET のクラスライブラリが使えるのはとてもいいです。シェルスクリプトだと、XML をパースして書き換えるとか、接続文字列を書き換えるとかちょっとしんどいんですね。

  • PowerShell になじめない (おっさんだから? 新しいプログラム言語は覚えられるから違うと思うけど...)
  • Linux で動かない (わけじゃないけど、シェルスクリプトほど手軽ではない)
  • インスタンスメソッドと関数で挙動が違う
  • バックグラウンドでジョブを実行するだけでなんでこんなにくろうするんや...

Cake

Windows でも Linux でも動くビルドツールということで、紹介してもらった Cake も使いました。

検索すると PHP の方が出てくるので、Cake Build で検索するのをお勧めします。

Cake はほぼ C# でビルドスクリプトが書けます。Cake Script は (おそらく) C# のコードに変換され、ビルドされ、.NET 上で実行されます。

var target = Argument("target", "Build");

Task("Build")
    .Does(() =>
    {
        DotNetCoreBuild(".");
    });

RunTask(target);

公式・非公式を合わせると、様々なアドインが用意されていまして、かなり便利です。

でも、この Cake Script、Visual Studio や Rider のコード補完などの支援を得られないんですね。IDE の支援に頼っている身としてはかなり苦しい。

足りないアドインを Cake Script じゃなくて、アセンブリとして作れば IDE の支援が得られるのですが、そのアセンブリの管理がめんどくさいという!

NUKE

ひょんなことから発見したのが NUKE です。

使ってみてびっくり。Visual Studio のソリューションに _build って名前のプロジェクトと Build.cs が作られて、そのプロジェクトを実行するという強引さ!

image.png

普通の C# プロジェクトのなので、IDE のコード補完などの支援がバリバリ効きます!

シェルスクリプトほどお手軽ではありませんが、C# のコードを書く延長で書けるのはいいですね!

NUKE の使い方

セットアップ

セットアップの前に、Visual Studio でプロジェクトを作りましょう!

コマンドでやりたい人はこんな感じで。

$ mkdir app
$ cd app
$ dotnet new sln
$ dotnet new console --output ConsoleApp
$ dotnet sln add ConsoleApp

NUKE は .NET Tool 版がおすすめです。インストールしちゃいます。

$ dotnet new tool-manifest
$ dotnet tool install Nuke.GlobalTool

続いてセットアップです。いろいろと聞かれますが、とりあえずデフォルトでよいでしょう。ただ、GitVersionNo を選んでください。

$ dotnet nuke :setup:

NUKE Global Tool version 5.0.2 (Windows,.NETCoreApp,Version=v2.1)
Could not find root directory. Falling back to working directory.
How should the build project be named?
≫
¬  _build

...

Do you use GitVersion?
¬  No, custom versioning
Creating directory 'C:\Users\masakura\tmp\app\.\build'...
Creating directory 'C:\Users\masakura\tmp\app\source'...
Creating directory 'C:\Users\masakura\tmp\app\tests'...

ビルドスクリプトのプロジェクトができましたので、ビルドします。

$ dotnet nuke build

...

═══════════════════════════════════════
Target             Status      Duration
───────────────────────────────────────
Restore            Executed        0:01
Compile            Executed        0:01
───────────────────────────────────────
Total                              0:03
═══════════════════════════════════════

Build succeeded on 2021/02/18 19:36:15. \(^ᴗ^)/

ビルドできました!

ところで、横長の顔文字って開発者って日本人なんでしょうか?

ビルドのカスタマイズ

ビルドスクリプトのカスタマイズは build プロジェクトの Build.cs ファイルを修正します。

build/Build.cs
class Build: NukeBuild
{
    // ...

    Target Restore => _ => _
        .Executes(() =>
        {
            DotNetRestore(s => s
                .SetProjectFile(Solution));
        });

    Target Compile => _ => _
        .DependsOn(Restore)
        .Executes(() =>
        {
            DotNetBuild(s => s
                .SetProjectFile(Solution)
                .SetConfiguration(Configuration)
                .EnableNoRestore());
        });
}

=> _ => _ なんて、??? ってな書き方を要求されますが、そういうもんだと思ってください。(FAQ にも書かれているぐらい...)

dotnet nuke Restoredotnet nuke Compile でそれぞれのターゲットを実行できます。詳しくは 公式ドキュメントを読んでいただければ。

DotNetBuild() などの関数がタスクになります。タスクの一覧は API Reference にあるクラスのうち、Tasks で終わるものです。Cake と比べると少なめです。

タスクを利用する

ReShaper Command Line Tools を利用して、コード検査をしてみます。

build/Build.cs
class Build : NukeBuild
{
    // ...

    Target Lint => _ => _
        .DependsOn(Compile)
        .Executes(() =>
        {
            ReSharperInspectCode(s => s
                .SetTargetPath("app.sln")
                .SetOutput("inspection-report.xml"));
        });
}

そして、dotnet nuke Lint を実行します。と、エラーが出ます。

$ dotnet nuke Lint

...

Assertion failed: Could not find package 'JetBrains.ReSharper.GlobalTools' using:
 - Project assets file 'C:\Users\masakura\tmp\app\build\obj\project.assets.json'
 - NuGet packages config 'C:\Users\masakura\tmp\app\build\_build.csproj'
Run 'nuke :fix' to add missing references.

...

dotnet nuke :fix をしろと仰せなので、実行します。

$ dotnet nuke :fix

NUKE Global Tool version 5.0.2 (Windows,.NETCoreApp,Version=v2.1)
During last execution a package was found to be missing.
Which version of 'JetBrains.ReSharper.GlobalTools' should be added?
¬  2020.3.2 (latest release)
> "C:\Program Files\dotnet\dotnet.exe" --info
Added 'JetBrains.ReSharper.GlobalTools' to 'C:\Users\masakura\tmp\app\build\_build.csproj'.

.NET Tools 版の ReSharper Command Line Tools を _build プロジェクトに取り込んでくれます。

もう一度 dotnet nuke Lint とすると今度は成功します。

$ dotnet nuke Lint

Build succeeded on 2021/02/18 20:18:43. \(^ᴗ^)/

残念ですけど、あくまで ReSharper Command Line Tools を実行するタスクなので、コードに問題があってもビルドは失敗しません...

が、レポートファイルを読み込んで問題があった時は Fail() を呼び出すようターゲットを書き換えればよいでしょう。

パラメーター

パラメーターは以下のように書きます。

build/Build.cs
class Build: NukeBUild
{
    [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
    readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;

    // ...
}

dotnet nuke --configuration Release とすると、Configuration 変数に Release が入ります。

Compile ターゲットで、.SetConfiguration(Configuration) としていますので、リリースビルドになるわけです。

何気に IsLocalBuild プロパティが便利でして。これ、開発者のパソコンでビルドされたかどうかを検出してくれます。つまり、CI でビルドされているときはリリースビルドになるんですね。\(^ᴗ^)/

パス

少し面白いなあと思ったのが、パス。ディレクトリとかファイルとか。

build/Build.cs
class Build : NukeBuild
{
    // ...

    AbsolutePath SourceDirectory => RootDirectory / "source";
    AbsolutePath TestsDirectory => RootDirectory / "tests";
    AbsolutePath OutputDirectory => RootDirectory / "output";

    // ...
}

おペーレーターオーバーロードで / を上書きしてそれっぽく書けるようになっています。

外部ツールを呼び出す

Lint ターゲットで出力したレポートを Codeclimate 形式の JSON ファイルに変換します。

build/Build.cs
class Build : NukeBuild
{
    // ...

    [PackageExecutable("resharper-to-codeclimate", "resharper-to-codeclimate.dll")]
    readonly Tool ReSharperToCodeClimate;

    Target Lint => _ => _
        .DependsOn(Compile)
        .Executes(() =>
        {
            ReSharperInspectCode(s => s
                .SetTargetPath("app.sln")
                .SetOutput("inspection-report.xml"));
            ReSharperToCodeClimate("inspection-report.xml inspection-report.json");
        });
}

PackageExecutable で NuGet にあるパッケージを実行することができます。ツールを追加したときは dotnet :fix を忘れないでください。

$ dotnet nuke Lint

Build succeeded on 2021/02/18 20:31:53. \(^ᴗ^)/

inspection-report.json ファイルができていると思います。\(^ᴗ^)/

ほかにも、実行ファイルを呼び出すこともできます。詳しくは Lightweight Integration をご覧ください。

余談ですが、GitLab は Codeclimate 形式のレポートを Merge Request (GitHub の Pull Request) に表示する機能があります。興味がある方は Code Quality をどうぞ。何気に便利です。

使ってみて

シェルスクリプトなどと比べると、起動に少し時間がかかるのが残念ですが、とても書きやすいので満足しています。複雑な処理を書く時も C# のコードそのままですし、何といっても IDE のコード補完やコード検査にコード整形ががっつりききます。

ばりばりビルドスクリプトを書きたい人はおすすめです。

タスクが少ないのはあれですが、サクッと C# でコード書いちゃえばいいだけなので、あまり気になりませんし。

GitVersion との連携も予想外に便利でした。またの機会に開設しようかと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
2
Help us understand the problem. What are the problem?