【Unity】Source Generatorでスクリプト以外のファイルにアクセスする
Unity向けにSource Generatorを実装する場合、APIを介してアクセスできるのはコンパイル対象のソースファイルのみになります。しかしソースファイル以外も参照してコード生成したい場面は多々あるので、いくつかの方法を紹介します。
AdditionalFilesを使う
C#コンパイラにはAdditional Filesという仕組みがあり、Roslyn AnalyzerやSource Generatorに追加の依存ファイルを読み込ませることができます。
https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Using%20Additional%20Files.md
UnityではAssets/csc.rsp
というテキストファイルにC#コンパイラのオプションを記述することができます。
/additionalfile:Assets/hoge.txt
AdditionalFilesに指定したファイルは、Source Generator側ではこんな感じのコードでアクセスすることができます。
public void Execute(GeneratorExecutionContext roslynContext)
{
foreach (var file in roslynContext.AdditionalFiles)
{
Console.WriteLine(file.Path);
}
}
ただこの方法では、AdditionalFilesに指定したファイルが更新された際にコンパイルが実行されない問題があります。そこはAssetModificationProcessor
などを用いてファイルの更新にフックしてコンパイルを実行する枠組みを整える必要があります。
またこの方法では、パスに空白を含むファイルを指定することができません。これはUnity特有の不具合で、本来C#コンパイラ的にはファイルパスを二重引用符で囲むとちゃんとパースしてくれるはずなんですが……
バグレポートも出したのですが残念ながらWon't Fixになってしまいました。
Unityの将来バージョンでSDK style csprojが来てくれると、csprojの中にAdditionalFilesを記述できるのでいろいろと問題が解決するんじゃないかな~という希望を持っています。
ファイルを直接読み込む
Source Generatorといえど普通のC#プログラムなので、System.IOなど通常のファイル操作APIが使えます。
たとえばこんな風に属性を設定してSource Generatorでパスが取得できれば、System.IO.File.ReadAllText()
などでファイルを読むことができます。
[GenerateSomething("hoge.txt")]
public partial class Hoge
{
}
こちらの方法もファイルの更新にフックしてコンパイルを実行させる必要があります。
また、こちらの方法ではどのパスを基準にするのかという問題があります。
ソースファイルのパスを基準にする
Source Generatorでは処理対象のC# ソースファイルのパスを得ることができます。
そこからの相対パスを指定するような形で目的のファイルパスを解決することができます。
プロジェクトパスを頑張って取得する
Source GeneratorのAPIでUnityプロジェクトのパスを取得する手段はどうやらないようです。
しかし前述の通りコンパイル対象のC# ソースファイルのパスは得ることができるので、たとえばAssets直下にスクリプトを配置しておき、そのパスから辿るといったことは可能です。ただしSource Generatorはコンパイル対象のアセンブリごとに独立して実行されるため、対象のスクリプトがアセンブリに含まれているかどうかを意識する必要があります。また、UPMパッケージの場合はインストール方法によってパッケージの配置されるディレクトリが異なるため、正しくプロジェクトパスを取得できないことがあります。
余談:AssetDatabaseとかUnityのAPIは使えないの?
やらないほうがいいと思います。Source GeneratorはUnityエディタとは独立したプロセスで動作し、IDE上での編集時にも必要であるため、Unityエディタの状態にかかわらず正常に動かせることが望ましいからです。IPCなどすれば不可能ではないかもしれませんが……
もしどうしても必要ならば、AssetModificationProcessor
などを用いてコード生成を行い、結果をcsファイルとして出力するような(旧来の)コード生成方式を使用するのがよいかと思います。
Discussion