複数ラウンド処理
KSPは、複数ラウンド処理、つまり複数ラウンドにわたってファイルを処理することをサポートしています。これは、後続のラウンドが前のラウンドの出力を追加の入力として使用することを意味します。
プロセッサへの変更
複数ラウンド処理を使用するには、SymbolProcessor.process()
関数が無効なシンボルのために遅延されたシンボルのリスト(List<KSAnnotated>
)を返す必要があります。KSAnnotated.validate()
を使用して、次のラウンドに遅延される無効なシンボルをフィルタリングします。
次のサンプルコードは、validationチェックを使用して無効なシンボルを遅延する方法を示しています。
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder")
val result = symbols.filter { !it.validate() }
symbols
.filter { it is KSClassDeclaration && it.validate() }
.map { it.accept(BuilderVisitor(), Unit) }
return result
}
複数ラウンドの動作
次のラウンドへのシンボルの遅延
プロセッサは、特定のシンボルの処理を次のラウンドに遅延できます。シンボルが遅延されると、プロセッサは他のプロセッサが追加の情報を提供してくれるのを待ちます。必要な回数だけシンボルを遅延し続けることができます。他のプロセッサが必要な情報を提供すると、プロセッサは遅延されたシンボルを処理できます。プロセッサは、必要な情報が不足している無効なシンボルのみを遅延する必要があります。したがって、プロセッサはクラスパスからシンボルを遅延するべきではありません。KSPは、ソースコードからのものではない遅延されたシンボルもフィルタリングします。
例として、アノテーション付きクラスのビルダーを作成するプロセッサは、そのコンストラクタのすべてのパラメータ型が有効である(具体的な型に解決される)ことを必要とする場合があります。最初のラウンドでは、パラメータ型の1つが解決できません。次に、2番目のラウンドでは、最初のラウンドで生成されたファイルのために解決可能になります。
シンボルの検証
シンボルを遅延する必要があるかどうかを判断する便利な方法は、validationを使用することです。プロセッサは、シンボルを適切に処理するために必要な情報を知っている必要があります。validationには通常、コストのかかる解決が必要となるため、必要なものだけを確認することをお勧めします。前の例に続いて、ビルダープロセッサの理想的なvalidationは、アノテーション付きシンボルのコンストラクタのすべての解決されたパラメータ型にisError == false
が含まれているかどうかのみをチェックします。
KSPは、デフォルトのvalidationユーティリティを提供します。詳細については、Advancedセクションを参照してください。
終了条件
複数ラウンド処理は、完全なラウンドの処理で新しいファイルが生成されなくなると終了します。終了条件が満たされたときに未処理の遅延シンボルがまだ存在する場合、KSPは未処理の遅延シンボルを持つ各プロセッサに対してエラーメッセージをログに記録します。
各ラウンドでアクセス可能なファイル
新しく生成されたファイルと既存のファイルの両方に、Resolver
を通じてアクセスできます。KSPは、ファイルにアクセスするための2つのAPIを提供します:Resolver.getAllFiles()
とResolver.getNewFiles()
。getAllFiles()
は既存のファイルと新しく生成されたファイルの両方の結合されたリストを返し、getNewFiles()
は新しく生成されたファイルのみを返します。
getSymbolsAnnotatedWith()への変更
シンボルの不要な再処理を避けるために、getSymbolsAnnotatedWith()
は、新しく生成されたファイルで見つかったシンボルと、最後のラウンドからの遅延シンボルからのシンボルのみを返します。
プロセッサのインスタンス化
プロセッサインスタンスは1回だけ作成されます。つまり、プロセッサオブジェクトに情報を格納して、後のラウンドで使用できます。
ラウンド間で一貫した情報
すべてのKSPシンボルは、解決の結果が前のラウンドで生成されたものに基づいて変更される可能性があるため、複数のラウンドで再利用できません。ただし、KSPは既存のコードの変更を許可していないため、シンボル名の文字列値など、一部の情報は再利用できるはずです。要約すると、プロセッサは前のラウンドからの情報を格納できますが、この情報が将来のラウンドで無効になる可能性があることに留意する必要があります。
エラーと例外の処理
エラー(プロセッサがKSPLogger.error()
を呼び出すことによって定義される)または例外が発生すると、現在のラウンドが完了した後、処理は停止します。すべてのプロセッサがonError()
メソッドを呼び出し、finish()
メソッドは呼び出しません。
エラーが発生した場合でも、他のプロセッサはそのラウンドの処理を通常どおり続行することに注意してください。これは、エラー処理がラウンドの処理が完了した後に発生することを意味します。
例外が発生した場合、KSPはKSPからの例外とプロセッサからの例外を区別しようとします。例外が発生すると、処理は直ちに終了し、KSPLoggerにエラーとして記録されます。KSPからの例外は、さらなる調査のためにKSP開発者に報告する必要があります。例外またはエラーが発生したラウンドの最後に、すべてのプロセッサがonError()関数を呼び出して、独自のエラー処理を行います。
KSPは、SymbolProcessor
インターフェースの一部として、onError()
のデフォルトのno-op実装を提供します。このメソッドをオーバーライドして、独自のエラー処理ロジックを提供できます。
高度な内容
validationのデフォルトの動作
KSPによって提供されるデフォルトのvalidationロジックは、validationされているシンボルのエンクロージャスコープ内のすべての直接到達可能なシンボルをvalidationします。 デフォルトのvalidationは、エンクロージャスコープ内の参照が具体的な型に解決可能かどうかをチェックしますが、validationを実行するために参照型を再帰的に掘り下げることはありません。
独自のvalidationロジックの記述
デフォルトのvalidation動作は、すべての場合に適しているとは限りません。KSValidateVisitor
を参照し、カスタムのpredicate
ラムダを提供することで独自のvalidationロジックを記述できます。これは、チェックする必要のあるシンボルをフィルタリングするためにKSValidateVisitor
によって使用されます。