C# でシングルバイナリアプリケーションを作成する

.NET Core 3.0 でシングルバイナリを作れるようになるオプションが追加されました。自分的には、シングルバイナリを作りたいときは今までは常にGoでプログラムを書いてきました。それはリリースやインストールが圧倒的に楽だからです。これがC#でできるようになることはうれしいことです。以前から存在は知っていましたが、試したことはなかったので、実際に使ってアプリを作ってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/9c542ed7-5a9b-1dd8-8ecb-903f73dbdaa4.png)

# Self Contained アプリケーション

最初は名前からしてこれが該当設定と思っていました。.NET Core のコンソールアプリケーションを `Self-Contained`にしてみます。`csproj` ファイルに次の設定を入れてみましょう。ちなみに `Self Contained` は DotNet のランタイムを成果物に含むという意味で１つのバイナリファイルという意味ではありません。`Self Contained` を１つのファイルにパッケージすることで、シングルバイナリのアプリケーションが出来上がります。

```csproj
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
    <PublishReadyToRun>true</PublishReadyToRun>
    <PublishSingleFile>true</PublishSingleFile>
  </PropertyGroup>
```

|Property | Description | Possible Value | Default |
| ------- | ----------------------------- | ------- | ----- |
| OutputType | 出力タイプのフォーマット | Library, Exe, Module, Winexe | Library |
| TargetFramework | アプリケーションを実行するのに必要な .NET のフレームワーク | netcore3.1 等 [詳細](https://docs.microsoft.com/en-us/dotnet/standard/frameworks) | N/A |
| RuntimeIdentifier | Publish で使われるランタイム識別子、OSの種類やアーキテクチャを指定する | win10-x64 [詳細](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog) | N/A |
| PublishReadyToRun | ILに加えて、ネイティブにコンパイルされたコードを含む。現在のところクロスプラットフォームビルドはサポートされていない。[詳細](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#readytorun-images) | true/false | false |
| PublishSingleFile | アプリケーションを１つのExeにまとめる | true/false | false |

この設定をして`dotnet publish`コマンドを実行すると`win10-x64`用のアプリケーションができます。

```bash
$ dotnet publish
```

`PublishReadyToRun` をするとすでにネイティブにコンパイルされているバイナリを含むので起動時間が早くなります。ただし、現在のところクロスプラットフォームビルドはサポートされていません。具体的にはWindows10で、Linuxのビルドをするときにこのオプションは現在のバージョン(3.1.301)では使えないようです。CIでビルドする場合は、ビルドしたい対象のプラットフォームのビルドを使うとよさげです。ただ、このオプションの指定はサイズを増加させるようです。リリースのコマンドでオプションを指定して試してみましょう。

```powershell
PS > dotnet publish -c Release -r win10-x64 -p:PublishSingleFile=true -p:PublishReadyToRun=true
PS > ls C:\Users\tsushi\source\repos\KafkaSpike\KafkaSpike\bin\Release\netcoreapp3.1\win10-x64\publish\
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          7/4/2020   1:32 PM       76303080 ProducerSample.exe
-a----          7/4/2020   1:32 PM           1588 ProducerSample.pdb

PS > dotnet publish -c Release -r win10-x64 -p:PublishSingleFile=true -p:PublishReadyToRun=false
PS > ls C:\Users\tsushi\source\repos\KafkaSpike\KafkaSpike\bin\Release\netcoreapp3.1\win10-x64\publish\
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          7/4/2020   1:32 PM       75643254 ProducerSample.exe
-a----          7/4/2020   1:32 PM           1588 ProducerSample.pdb
```
思ったより大したことなさそうですね。ちなみに、２つのコマンドの間には、`Remove-Item -Path .\bin -Recurse` などで`bin`を削除してからやるほうがよさそうです。しないと同じ結果になりました。

Linuxもビルドしてみましょう。ただし、この場合は、`PublishReadyToRun` Windows10だと使えませんので、

```powershell
PS > dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true -p:PublishReadyToRun=false
PS  > ls C:\Users\tsushi\source\repos\KafkaSpike\KafkaSpike\bin\Release\netcoreapp3.1\linux-x64\publish\

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          7/4/2020   1:38 PM      137016076 ProducerSample
-a----          7/4/2020   1:38 PM           1588 ProducerSample.pdb
```
サイズでけぇなぁｗ　でも、Linux 用のビルドもできるのはありがたい。137MB で、Windows の 75MB の倍ぐらいですね。

# Visual Studio 

最後に、Visual Studio でも設定ができますので、見てみましょう。Project ファイル右クリック > Publish を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/757b5fcc-e760-d91a-c098-958242a1baad.png)

これで、一通りのシングルバイナリ系の設定が理解できたと思います。次が、シングルバイナリにほしくなる、コマンドラインパーサーの調査をしてみたいと思います。

参考までにリソースのパートに私が実際に使ったサンプルのURLを張っておきますね。

# リソース

* [dotnet publish](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish)
* [ReadyToRun image](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#readytorun-images)
* [.NET Core RID Catalog](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog)
* [Single-file executables - What's new in .NET Core 3.0](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#single-file-executables)
* [Single-file Publish = dotnet/designs](https://github.com/dotnet/designs/blob/master/accepted/2020/single-file/design.md)
* [Common MSBuild project properties](https://docs.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-properties?view=vs-2019)
* [Target frameworks in SDK-style projects](https://docs.microsoft.com/en-us/dotnet/standard/frameworks) 
* [Sample](https://github.com/TsuyoshiUshio/KafkaSpike/blob/master/KafkaSpike/ProducerSample.csproj)