C# - 単一実行可能ファイル (.NET Core 3)

.NET Core 3.0では、デフォルトのビルド出力がexeファイルになった。
もろもろの依存関係をまとめた単一実行可能ファイルも出力できるようになった。(前からできたかは知らない)

手順

以下実行する。

dotnet publish -r win-x64 /p:PublishSingleFile=true

または、*.csproj のPropertyGroupに以下を追記。

<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>

win-x64の部分をlinux-x64osx-x64に置き換えるとそれぞれの環境向け単一実行ファイルが出力される。

ファイルサイズについて

RuntimeIdentifierを指定すると、ランタイムこみこみのいわゆる自己完結型(配布先に.NET Coreがインストールされていなくても動く形式)が出来上がるので、ファイルサイズが大きくなりがち。
これを削減する方法がいくつかある。

  • ランタイムを含めない
  • PublishTrimmedする
  • サードパーティツールを使う

ランタイムを含めない

--self-contained=falseを追加してdotnet publishすればOK

PublishTrimmed

/p:PublishTrimmed=trueを追加してdotnet publishする。
または、*.csproj のPropertyGroupに以下を追記。

<PublishTrimmed>true</PublishTrimmed>

サードパーティツールを使う

warpというのがあって、PublishTrimmedするより小さくなるらしい

ReadyToRun

.NET Core アプリケーションの起動時間を向上させる施策らしい。ファイルサイズは少し増える。

やり方は/p:PublishReadyToRun=trueを追加してdotnet publishするか、*.csproj のPropertyGroupに以下を追記。

<PublishReadyToRun>true</PublishReadyToRun>

ただし、基本的にはビルドプラットフォームとターゲットプラットフォームが異なる場合には使えない(今のところWindows上でLinuxのReadyToRunはできない)。

現状の単一実行可能ファイルは初回実行時にtempディレクトリにファイルを展開してから実行されるので、一回目の実行は割と起動が遅くて二回目以降から速くなる。
ReadyToRunは二回目以降の起動に効いてくると思えばよいのだろうか。

実行ファイルパス問題

上に書いたように、単一実行可能ファイルはtempディレクトリに展開され実行されるので実行パスがおかしなことになる。
例えば、実行ファイルのパスが欲しくてプログラム中でAppContext.BaseDirectoryとかしても、ファイルが展開されているtempディレクトリのパスを取得してしまう。

実行ファイルのパスは以下で取得できる。

var ExePath = Process.GetCurrentProcess().MainModule.FileName;

これはdotnet xxx.dllで実行していた場合にはdotnetコマンドのパスが取得できるアレですね。

参考リンク

以上


See also