csc で Unity 用 DLL を作る

表題の通り、csc で DLL を作成するにあたって調べた内容を備忘録として残します。 以前書いた記事の続編的な内容です。

ryu-rand.hatenablog.com

検証した環境は次の通りです。

はじまりはじまり

事の起こりは、mcs による DLL 作成時に下記のエラーが起きたことです。

error CS1644: Feature `default literal' cannot be used because it is not part of the C# 7.0 language specification

原因になったのは次のような書き方です。

public void Hoge(Vector3 position = default)

原因はエラーで指摘されているように、default リテラルC# 7.1 以降でないと利用できないことです。 どうやら、mcs はデフォルトだと C# 7.0 以前のものを利用しているようです。

docs.microsoft.com

エラーの解消

この現象の解決策は二つあります。 一つは型推論に頼らず、スクリプトで型を記述することです。

public void Hoge(Vector3 position = default(Vector3))

もう一つは、mcs 実行時に言語バージョンをオプションで指定することです。

docs.microsoft.com

csc への乗り換え

前記事で検証したとおり、Unity 2018 ではコンパイラとして csc が利用されています。 せっかく DLL 作成コマンドを修正するのであれば、そのまま Unity と挙動を合わせたいと考えました。

まず、Unity 内部で利用されている csc は次のものです。(ファイルをリネームするとエラーになります)

/Applications/Unity/Hub/Editor/2018.4.9f1/Unity.app/Contents/Tools/Roslyn/csc

試しに現在使っている mcs と入れ替えると、次のエラーが大量に発生します。

error CS0518: 定義済みの型 'System.Void' は定義、またはインポートされていません
error CS0518: 定義済みの型 'System.ValueType' は定義、またはインポートされていません
error CS0518: 定義済みの型 'System.Object' は定義、またはインポートされていません

Visual Studio のオプションを流用

対処としては、必要な DLL を参照するオプションをつける必要があります。 具体的な内容は、前回の記事Visual Studio を用いて DLL を作成した際の処理を参考にします。

  • mscorlib.dll
  • System.dll
  • SystemCore.dll

これら三つを参照することで先ほどのエラーは起きなくなります。

その他の気になるオプション

そのほかの参照に関連するオプションとして次のものがあります。

結論から言うと、今回の検証の中ではこれらがあってもなくても挙動は変わりませんでした。

まず、noconfigcsc.rsp という参照を記述するファイルの設定を無視するものです。 ただし、利用している csc と同じディレクトリには同ファイルは存在していませんでした。

nostdlib+mscorlib を参照しなくなるオプションです。 Visual Studiio の例では mscorlib を明示的に指定しているため、いずれにせよ参照は必要だと思われます。

今回の場合、意図せぬ参照が発生するよりは必要最低限に絞った方が良いと判断し、これらも付与したコマンドにしていきました。

参照するべき DLL の見極め

Unity 内部で System.dll を検索すると、複数のファイルが存在します。

./Tools/Roslyn/System.dll
./NetStandard/compat/2.0.0/shims/netfx/System.dll
./MonoBleedingEdge/lib/mono/4.5.2-api/System.dll
./MonoBleedingEdge/lib/mono/unity/System.dll
./MonoBleedingEdge/lib/mono/4.7.1-api/System.dll
./MonoBleedingEdge/lib/mono/4.5.1-api/System.dll
./MonoBleedingEdge/lib/mono/4.5-api/System.dll
./MonoBleedingEdge/lib/mono/4.6.2-api/System.dll
./MonoBleedingEdge/lib/mono/2.0-api/System.dll
./MonoBleedingEdge/lib/mono/4.6-api/System.dll
./MonoBleedingEdge/lib/mono/4.6.1-api/System.dll
./MonoBleedingEdge/lib/mono/4.5/System.dll
./MonoBleedingEdge/lib/mono/4.0-api/System.dll
./MonoBleedingEdge/lib/mono/unityjit/System.dll
./MonoBleedingEdge/lib/mono/unity_web/System.dll
./MonoBleedingEdge/lib/mono/unityaot/System.dll
./Mono/lib/mono/unity/System.dll
./Mono/lib/mono/2.0/System.dll
./Mono/lib/mono/unity_web/System.dll

いくつかそれっぽい DLL で検証したところ、次のような結果となりました。

DLL 成否 メモ
./MonoBleedingEdge/lib/mono/4.7.1-api/System.dll 成功 Visual Studio が使っていたものに近いバージョン
./Tools/Roslyn/System.dll 失敗 System 関連でエラーのまま
./MonoBleedingEdge/lib/mono/unity/System.dll 失敗 Task 関連のエラー、C# 7.0 未満?

Roslyn のものなら上手くいくのではと思っていたので、ここは少し予想外でした。 ただし、上手くいったファイルと比べてみると Roslyn のものはファイルサイズがかなり小さいです。(結果は一部割愛です)

$ ls -a /Applications/Unity/Hub/Editor/2018.4.9f1/Unity.app/Contents/MonoBleedingEdge/lib/mono/4.7.1-api/System.dll
admin  500224

$ ls /Applications/Unity/Hub/Editor/2018.4.9f1/Unity.app/Contents/Tools/Roslyn/System.dll
admin  41472

どちらのファイルも、リネームして参照できない状態にすると Unity でエラーが発生します。 また、Roslyn の System.dll をリネームする(参照できない状態にする)と、4.7.1-api/System.dll を使おうとした時でもエラーが発生します。

このことから、標準的な System の実装は 4.7.1-api/System.dll に定義されており、Roslyn 用の実装が Roslyn/System.dll に定義されていると推測されます。 (これらを示すドキュメントを確認できた訳ではないので、あくまで推測です)

結論

ここまでの検証結果を考慮し、次のようなコマンドを用意しました。

Mono := $(UnityDirectory)/Contents/MonoBleedingEdge/lib/mono/4.7.1-api
Csc := $(UnityDirectory)/Contents/Tools/Roslyn/csc
LangVersion := 7.3

make_dll:
  $(Csc) \
   -r:$(UnityDirectory)/Contents/Managed/UnityEngine.dll \
   -r:$(Mono)/mscorlib.dll \
   -r:$(Mono)/System.Core.dll \
   -r:$(Mono)/System.dll \
   -noconfig \
   -nostdlib+ \
   -target:library \
   -recurse:'Assets/Scripts/*.cs' \
   -out:AROW.dll \
   -langversion:$(LangVersion)

このコマンドで DLL を作成できること、普段行っているテストに関して不備がないこと、は一通り確認できました。 もし何か不備が見つかれば対応と合わせて追記します。

ポエム

Unity で行われているコンパイル部分が公開されればありがたいなと思いつつ、一般的に Unity で扱うアセットは Asset Store かオープンソースでの公開が主流な印象があるため、DLL 作成はそこまで強く想定されていないのかなと感じた年末です。